memorymappedfile.createfromfile 不能直接映射部分区域,因其默认创建完整文件映射且未指定偏移与大小参数;需用 createviewaccessor 配合预对齐 offset(4096 倍数)和合理 capacity 实现分段映射。

MemoryMappedFile.CreateFromFile 为什么不能直接映射部分区域
因为 CreateFromFile 默认创建的是「完整文件映射」,底层调用 Windows 的 CreateFileMapping 时未指定 dwMaximumSizeHigh/dwMaximumSizeLow 和偏移参数,所以无法控制起始位置和长度。真正支持部分映射的是 CreateFromFile 的重载或更底层的 MemoryMappedFile.Create + CreateViewAccessor 组合。
如何用 CreateViewAccessor 映射文件的指定字节范围
必须先创建内存映射对象(可读/写/复制),再通过 CreateViewAccessor 指定偏移与大小。关键点在于:视图的偏移必须是系统页面大小(通常是 4096)的整数倍,否则抛出 ArgumentException;长度可以任意,但超出文件实际大小会触发访问异常。
-
offset参数必须对齐到Environment.SystemPageSize,例如想从第 8192 字节开始映射,没问题;但从 8200 开始就会报错 -
capacity是视图可见长度,不是必须等于剩余文件长度,但读写越界会引发IOException或AccessViolationException - 若文件以
FileMode.Append打开或未预分配空间,映射后写入超出当前文件长度的位置,需先调用FileStream.SetLength扩容,否则写操作失败
using var fs = new FileStream("data.bin", FileMode.Open, FileAccess.ReadWrite);
using var mmf = MemoryMappedFile.CreateFromFile(fs, null, 0, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, false);
// 从第 16KB 处开始,映射 4KB 视图(offset=16384 必须是 4096 的倍数)
using var accessor = mmf.CreateViewAccessor(16384, 4096, MemoryMappedFileAccess.ReadWrite);
accessor.Write(0, (byte)42); // 写入视图起始位置
映射只读区域时要注意 FileAccess 和 MemoryMappedFileAccess 的匹配
如果原始 FileStream 是只读打开的,即使 MemoryMappedFileAccess 设为 ReadWrite,CreateViewAccessor 仍会成功,但后续写操作会抛出 UnauthorizedAccessException。Windows 层面的访问权限由底层句柄决定,上层设置无效。
- 确保
FileStream的FileAccess与后续MemoryMappedFileAccess一致,尤其在只读场景下不要误传ReadWrite - 若需多个进程共享只读视图,建议显式使用
FileAccess.Read+MemoryMappedFileAccess.Read,避免运行时权限冲突 - 映射后修改文件长度(如
SetLength)在只读流下会直接失败,必须用可写流打开
大文件分块映射时为何要避免频繁创建/释放 ViewAccessor
每次调用 CreateViewAccessor 都会触发一次 MapViewOfFileEx 系统调用,并占用一个虚拟内存区域。在 32 位进程或内存紧张环境下,频繁映射不同区域可能导致 OutOfMemoryException,即使物理内存充足——因为虚拟地址空间碎片化。
- 优先复用同一个
MemoryMappedFile实例,按需切换ViewAccessor,而不是为每段都新建MemoryMappedFile - 若需同时访问多个不重叠区域,可创建多个
ViewAccessor,但务必及时Dispose,否则虚拟内存泄漏比托管内存更隐蔽 - 注意
ViewAccessor不是线程安全的,多线程并发读写同一视图需自行加锁,或为每个线程分配独立视图










