mmap 并非总比 open().read() 快,仅在随机访问、多进程共享或避免全载入内存时有优势;顺序读取时因缺页中断反而更慢。

为什么 mmap 读大文件不总比 open().read() 快
直接结论:mmap 不是银弹,它只在特定场景下有优势——比如随机访问、多进程共享、或避免一次性加载整个文件到 Python 堆内存。如果你只是顺序读一遍再丢弃,mmap 往往更慢,还可能触发缺页中断拖累性能。
根本原因在于:mmap 把文件“映射”进虚拟地址空间,但实际数据仍留在磁盘,首次访问某页时才发生缺页中断并加载(lazy loading)。而顺序读用 read() 配合系统缓冲区(os.read 或内置 BufferedReader)通常更贴近底层 I/O 调度逻辑。
- 适合
mmap的场景:seek()频繁跳转、多个进程同时读同一文件、需要像操作字节数组一样切片(如mm[1024:2048]) - 不适合的场景:单次流式解析 CSV/JSON、日志逐行扫描、文件小于 100MB 且内存充足
-
mmap占用的是虚拟内存(VM),不是物理内存;但若大量页被访问,仍会挤占真实内存,引发OSError: Cannot allocate memory
mmap.mmap() 的三个关键参数怎么选
最常踩坑的是 length 和 access 参数。它们不是可选的“优化项”,而是直接影响行为和安全性的硬约束。
-
length:必须显式指定,不能传0(除非你确定文件大小不变且想映射全部)。推荐用os.path.getsize(path)获取后传入,否则可能映射失败或截断 -
access:默认mmap.ACCESS_READ安全;若需修改文件内容,必须用mmap.ACCESS_WRITE,且文件需以r+模式打开;误用ACCESS_COPY可能导致写操作不落盘 -
fileno:必须是已打开文件的句柄(int),不能传文件路径字符串。常见错误:mmap.mmap(-1, ...)(句柄无效)或传了已关闭的file.fileno()
示例正确写法:
import mmap
import os
with open("bigfile.bin", "r+b") as f:
size = os.path.getsize(f.name)
mm = mmap.mmap(f.fileno(), length=size, access=mmap.ACCESS_READ)
用 mmap 读文本行(for line in mm)为什么报错
因为 mmap 对象不是 io.TextIOBase 子类,不支持直接迭代行。它返回的是 bytes 类型的内存视图,没有内置换行符识别逻辑。
- 错误现象:
TypeError: a bytes-like object is required, not 'mmap.mmap'或循环卡死 - 正确做法:先转为
io.BytesIO(mm)再按行读,或手动找b" "位置切片(更省内存) - 注意编码:
mm[100:200].decode("utf-8")比mm.read(100).decode()更安全,后者会移动内部指针影响后续访问 - 如果文件含混合编码(如 Windows 日志),
mmap无法自动检测 BOM,得自己判断开头字节再解码
Windows 下 mmap 映射失败的典型错误和绕过方式
Windows 对内存映射限制更严格,尤其对大文件或网络路径,常见错误是 WindowsError: [Error 8] Not enough storage is available to process this command(即使物理内存充足)。
- 根本原因:Windows 默认每个进程用户态虚拟地址空间仅 2GB(32 位)或受
/LARGEADDRESSAWARE标志限制(64 位下也非无限) - 解决方向:优先用
length分块映射(如每次映射 512MB),而非一次全量;避免映射超过 2GB 的单个mmap对象 - 路径陷阱:SMB 共享路径(
\servershareile)在 Windows 上不支持mmap,必须先copy到本地 NTFS 卷 - 替代方案:对纯读场景,用
io.BufferedReader+readinto(bytearray)组合,也能接近零拷贝效果,且跨平台稳定
复杂点始终在边界上:文件是否可写、是否被其他进程锁住、操作系统虚拟内存策略、以及你真正需要的是“快”还是“省内存”。这些细节没对齐,mmap 就只是多了一层抽象开销。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











