mmap比os.ReadFile快因省去内核态到用户态拷贝且按需加载,避免GB级文件引发GC或OOM;但随机访问差时反慢,写入需msync才落盘。

Go mmap 大文件读写为什么比 os.ReadFile 快?
因为 mmap 把文件直接映射进虚拟内存,省去内核态到用户态的数据拷贝;而 os.ReadFile 会把整个文件读进 Go 的堆内存,对 GB 级文件极易触发 GC 压力甚至 OOM。
但注意:mmap 不是万能加速器——它只是“按需加载”,首次访问某页才会触发缺页中断,真正读磁盘。如果随机访问跨度大、局部性差,反而比顺序 read() 慢。
-
mmap后的内存可直接用[]byte切片操作,无需额外 copy - 写入后需调用
msync(对应 Go 的Sync())才保证落盘,否则只在 page cache 中 - 32 位系统下虚拟地址空间紧张,
mmap映射超 2GB 文件可能失败
如何用 golang.org/x/sys/unix 调用 mmap?
Go 标准库不提供跨平台 mmap 封装,必须用 x/sys/unix 手动调用系统 API。Linux/macOS 行为一致,Windows 需换用 syscall.VirtualAlloc + syscall.CreateFileMapping,此处只说 Unix 类系统。
关键步骤:打开文件 → 获取大小 → unix.Mmap → 操作切片 → unix.Munmap(或 defer)
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
-
prot参数常用unix.PROT_READ | unix.PROT_WRITE;只读用PROT_READ -
flags推荐unix.MAP_SHARED:修改会同步到文件;MAP_PRIVATE是写时复制,不落盘 - 偏移量
offset必须是页对齐的(通常4096),否则Mmap返回EINVAL - 返回的
[]byte底层数组就是映射内存,可直接copy(buf, mapped[1024:2048])
mmap 写入后为什么文件没更新?
常见错觉:写了 mapped[0] = 1,立刻 ls -l 看大小或用其他进程读,发现没变。本质是缓存策略问题。
- 即使
MAP_SHARED,写入也先到 page cache,不一定立即刷盘 - 其他进程能否看到,取决于它是否也用
mmap且映射同一文件、是否MS_SYNC或MS_ASYNC - 强制刷盘:调用
unix.Msync(mapped, unix.MS_SYNC);不阻塞用MS_ASYNC - 更彻底的方式:
unix.Fdatasync(int(fd))或file.Sync(),确保 metadata + data 全落盘
处理超大文件时怎么避免地址空间耗尽?
64 位系统虚拟地址足够,但别一股脑 mmap 整个 100GB 文件——Go runtime 可能因虚拟内存碎片无法分配大 slice,或触发 runtime: out of memory(不是物理内存不够,是 vma 区域不足)。
- 按需映射:例如每次
mmap128MB 的 chunk,处理完Munmap,再映射下一个 - 用
unix.Madvise(mapped, unix.MADV_DONTNEED)提示内核丢弃已用页,减少 RSS 占用 - 注意
mmap返回的切片不能逃逸到 goroutine 长期持有,否则 GC 无法回收对应 vma 区域 - 调试技巧:
cat /proc/<pid>/maps | grep your_file查看当前映射段数量和大小
真正麻烦的从来不是“怎么映射”,而是“什么时候 unmmap”和“谁负责 sync”。多 goroutine 并发读写同一映射区域时,得自己加锁或用 atomic,mmap 本身不提供并发安全。

















