os.ReadFile 读大文件易 OOM,因其一次性分配全部内存;应改用 bufio.Reader 分块读取,复用缓冲切片,避免内存暴涨。

用 os.ReadFile 读大文件会 OOM
Go 的 os.ReadFile 内部直接分配完整文件大小的内存,对几百 MB 以上的文件极易触发内存溢出。它适合配置文件、小日志等 ≤1 MB 场景,不是为大文件设计的。
实操建议:
- 单次读取 >50 MB 的文件时,必须放弃
os.ReadFile - 若需全文内容(如校验 hash),改用
io.Copy+bytes.Buffer或分块make([]byte, 64*1024)读取 - 注意
bytes.Buffer.Grow不会自动扩容到目标大小,要预估或循环扩容
用 bufio.Scanner 按行读大文件要注意缓冲区溢出
bufio.Scanner 默认最大行长度是 64 KB,超长行(如单行 JSON、base64 编码块)会直接报错 scanner: token too long。
实操建议:
- 用
scanner.Buffer(make([]byte, 64*1024), 16*1024*1024)手动扩大缓冲区,第二个参数是最大令牌长度 - 若行长度不可控,改用
bufio.Reader.ReadString('\n')或ReadBytes('\n'),自己处理截断和拼接 -
Scanner不适合二进制文件或无换行结构的数据
真正高效读大文件:用 bufio.Reader 配合固定大小 Read
这是最可控、内存稳定、吞吐高的方式,适用于日志分析、批量导入、流式处理等场景。
实操建议:
- 缓冲区大小选
32*1024到256*1024之间,太小增加系统调用次数,太大浪费内存 - 用
reader.Read(buf)循环读取,检查返回的n, err:当err == io.EOF表示结束,n == 0 && err == nil是合法但罕见的空读 - 避免在循环里反复
make([]byte, size),应复用切片(buf = buf[:cap(buf)])
file, _ := os.Open("huge.log")
defer file.Close()
reader := bufio.NewReader(file)
buf := make([]byte, 128*1024)
for {
n, err := reader.Read(buf)
if n > 0 {
process(buf[:n]) // 处理有效数据
}
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
}
内存映射 mmap 不是银弹,syscall.Mmap 使用门槛高
Go 标准库不提供跨平台 mmap 封装,需用 golang.org/x/sys/unix(Linux/macOS)或 golang.org/x/sys/windows(Windows),且要手动处理页对齐、保护标志、同步刷新等细节。
实操建议:
- 仅当需要随机访问超大文件(如数据库索引、视频帧跳转)且已熟悉底层 mmap 行为时才考虑
- 普通顺序扫描、过滤、转换场景,
bufio.Reader性能足够,代码更健壮 - 注意 mmap 在 Windows 上可能因文件锁、共享模式导致打开失败,错误信息类似
The requested operation cannot be performed on a file with a user-mapped section open
os.ReadFile 用在了不该用的地方。











