正确做法是用mmap.open()自动处理页对齐、错误包装和平台差异,返回的mmap.map可直接当[]byte使用,len(mm)准确,内部已处理map_populate和sigbus防护;映射后需手动查找换行符解析,不可直接传给bufio.scanner。

只读 mmap 映射超长文本文件必须用 mmap.Open()
自己调 syscall.Mmap 在 Windows 上必然失败,在 Linux 上极易 panic: invalid argument。根本原因不是你传错参数,而是它不校验页对齐(offset 必须是 4096 的整数倍)、不转换错误码、不处理平台差异。真实文件大小和配置里写的“1GB”可能差几十 MB,syscall.Mmap 会照单全收然后崩给你看。
正确做法是:mmap.Open("data.txt") 自动按 f.Stat().Size() 映射整个文件,返回的 mmap.Map 可直接当 []byte 用,len(mm) 和 mm.Len() 都准确。内部已处理 MAP_POPULATE、页对齐、错误包装,SIGBUS 概率大幅降低。
- 映射后别信“文件没被截断”,访问前必须确认
off + size - 不要用
append()向映射底层数组追加数据——Go 不拦截越界,触发的是SIGBUS而非panic - 若需多次小范围读取,建议提前用
unsafe.Slice(unsafe.Add(unsafe.Pointer(&mm[0]), offset), length)(Go 1.21+)构造子视图,避免重复计算指针
mmap.Map 当 []byte 用比 ReadAt() 更快
你在做日志行号定位、固定结构的二进制记录扫描,或需要频繁跳转查偏移时,直接用切片语法 mm[off:off+size] 比走 mm.ReadAt(buf, off) 更快。实测延迟低 10%–20%,因为少一层函数调用和参数检查。
但注意:这仅适用于只读场景。如果文件内容在映射期间被外部修改(比如另一个进程 truncate 或 write),内核会按需重新加载页,行为符合 POSIX mmap 语义;但 Go 层面不会主动通知你,得靠应用逻辑兜底(如定期 stat 文件大小)。
- 顺序遍历整个大文件时,
mmap反而更慢——缺页中断频繁,不如bufio.Reader+ 大缓冲区稳定 - 映射超大文件(>100GB)可能触发内核
vm.max_map_count限制,报错cannot allocate memory,需调高该值 - Windows 上
mmap-go底层用CreateFileMapping+MapViewOfFile,行为与 Linux 一致,无需额外适配
超长文本按行解析不能只靠 mmap
mmap 提供的是随机字节访问能力,不是行解析器。你拿到 mm[0:mm.Len()] 后,仍需自己实现换行符查找(\n、\r\n)和边界裁剪。别指望 mmap 自带 Scanner 行为。
常见错误是把 mmap.Map 直接传给 bufio.NewScanner() —— 它底层依赖 io.Reader,而 mmap.Map 虽实现了 io.ReaderAt,但不满足 io.Reader 的流式语义(无内部游标、不自动推进)。强行用会导致死循环或漏读。
- 正确方式:用
bytes.IndexByte()或bytes.IndexRune()在映射内存中找\n,手动切分 - 若需兼容
\r\n,建议先用bytes.ReplaceAll(mm[off:], []byte("\r\n"), []byte("\n"))(注意别改原映射,应拷贝后再处理) - 行长度不可控时(如混杂 base64 或 JSON),
mmap+ 手动扫描仍是最快方案,但务必设好上限,防止无限循环
虚拟内存与 GC 的隐性冲突
mmap 占用的是虚拟地址空间,不是物理内存。但 Go 的 GC 完全不感知 mmap 区域,一旦你只取某一段切片(如 mm[1MB:1MB+1KB])后丢弃引用,底层 page fault 可能引发不可预测延迟——尤其是该段刚被换出又突然访问时。
更麻烦的是:GC 回收切片不等于释放 mmap 区域。如果你没显式调 mm.Close(),哪怕所有引用都消失,mmap 区域仍驻留,直到程序退出。这在长期运行服务中容易积累泄漏。
- 必须配
defer mm.Close(),且不能依赖 GC 自动回收 - 映射几十 GB 文件时,32 位环境可能直接耗尽虚拟地址空间,64 位也要留意
ulimit -v设置 - 频繁创建/关闭 mmap 映射(比如每请求一次)比复用一个映射更耗资源,尤其在高并发下
实际用起来最易被忽略的点是:mmap 不是万能加速器,它只加速“跳转”,不加速“扫描”。 你真要按行读完整个超长文本,bufio.Reader + 64KB 缓冲通常比 mmap 更稳更快;只有当你反复查特定偏移(比如索引文件里存了每行起始位置),mmap 才真正发挥价值。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











