MemoryMappedFile.CreateFromFile 读大文件卡死因默认全映射触发大量页错误;应分块映射、避免ViewStream、慎用随机访问;跨进程需统一命名、同步原语和结构体布局。

MemoryMappedFile.CreateFromFile 为什么一读大文件就卡死
因为默认用 MemoryMappedFileAccess.Read 打开时,.NET 会尝试把整个文件映射进虚拟地址空间——但实际只按需提交物理页。问题出在「按需」上:一旦你用 UnsafeAccessor 或 Span<byte>.GetPinnableReference()</byte> 猛扫一遍,就会触发大量页错误 + 磁盘 I/O,表面看就是卡死。
- 真正该做的是分块映射:用
CreateFromFile的capacity参数控制单次映射大小(比如 64MB),再配合MemoryMappedViewAccessor的Offset和Size跳着读 - 别用
MemoryMappedViewStream做大文件读写——它内部有缓冲层,会额外拷贝,吞吐反而更低 - 如果只是顺序读,用
FileStream+BufferedStream可能更快;内存映射的优势在随机访问或跨进程共享同一份数据
两个进程怎么用同一个 MemoryMappedFile 安全通信
关键不是“怎么创建”,而是“怎么同步”。MemoryMappedFile 本身不提供原子读写或信号机制,必须自己配同步原语。
- 命名必须一致且带全局前缀:
MemoryMappedFile.OpenExisting("Global\MySharedData"),否则 Windows 默认在会话隔离命名空间里找,跨用户/服务进程根本看不到 - 共享区域里不能直接放 .NET 对象(比如
List<int></int>),只能放原始字节;结构体得用[StructLayout(LayoutKind.Sequential)]+[MarshalAs]显式对齐 - 必须搭配
EventWaitHandle或Mutex控制读写顺序,比如写进程写完后调用event.Set(),读进程等这个信号再读,否则大概率读到中间态数据
MemoryMappedViewAccessor.WriteArray 报“Attempted to read or write protected memory”
这是典型的指针越界或视图生命周期错乱。不是权限问题,是 MemoryMappedViewAccessor 已被释放,但你还拿着它的 IntPtr 在操作。
- 每次
WriteArray前检查accessor.SafeMemoryMappedViewHandle.IsInvalid == false - 不要缓存
accessor实例跨方法使用——它不是线程安全的,且底层句柄可能被 GC 回收 - 写入数组长度必须 ≤ 视图的
Capacity,且起始偏移 + 长度不能超限;用Math.Min(array.Length, accessor.Capacity - offset)做防御性截断
映射 10GB 文件时程序崩溃或提示“Not enough storage is available”
不是磁盘空间不够,是用户模式虚拟地址空间耗尽(32 位进程最多 2GB,64 位虽大但受 MAP_FAILED 类似限制)。Windows 对单个映射区也有上限,尤其在 Server 版本上更保守。
- 必须编译为 x64 平台(项目属性 → Build → Platform Target = x64),AnyCPU 不够稳
- 避免一次性映射整文件:用
CreateViewAccessor(offset, size, access)按需创建多个小视图,用完立刻Dispose() - 如果真要长期驻留大映射,考虑用
MemoryMappedFile.CreateNew+Allocate分配非文件后备内存,绕过文件 I/O 压力
最常被忽略的是:MemoryMappedFile 的资源释放依赖 Dispose(),而它内部持有一个 SafeMemoryMappedFileHandle。如果忘了 using 或没捕获异常就退出作用域,句柄泄漏会导致后续映射失败,且错误信息完全不提示根源。


















