readlines()会爆内存是因为它将整个文件一次性加载为字符串列表,内存占用达文件体积2–4倍;安全做法是用for line in f逐行迭代或f.read(chunk_size)分块读取。

直接用 open().readlines() 或 open().read() 读大文件,基本等于主动触发 MemoryError ——它不是 bug,是设计如此。
为什么 readlines() 会爆内存
因为 readlines() 返回的是一个完整列表,每行字符串都驻留在内存里。一个 5GB 的日志文件,按平均每行 200 字节算,就是约 2500 万行,Python 字符串对象本身还有额外开销,实际内存占用可能翻倍。系统 RSS 瞬间飙到 10GB+、进程被 OOM Killer 杀掉,都是常见结果。
常见错误现象包括:MemoryError、Killed: 9(macOS/Linux)、程序假死无响应、ps aux 显示 Python 进程 RSS 占用持续上涨。
按行读取:最安全的默认选择
适用于纯文本、结构清晰(如日志、CSV、JSONL)、无需随机跳转的场景。核心是让文件对象自己做迭代器,不缓存历史行。
立即学习“Python免费学习笔记(深入)”;
安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。
- ✅ 正确写法:
for line in f:—— 文件对象天然支持迭代,每次只加载一行 - ❌ 错误写法:
lines = f.readlines()或list(f)—— 全量加载,和read()一样危险 - 注意
line结尾带\n,需要时用line.rstrip('\n')去除,别用line.strip()(会误删首尾空格/制表符) - 如果必须跳过前 N 行(比如 CSV 表头),用
itertools.islice(f, N, None),比手动for _ in range(N): next(f)更健壮
分块读取:控制缓冲区大小的底层方案
适合二进制文件、或文本中存在超长行(导致单行就几百 MB)、或需自定义解析单元(比如按固定字节数切分协议包)的场景。
关键不是“块越大越快”,而是平衡 I/O 次数与内存压力:
- 推荐块大小:
8192(8KB)或65536(64KB);小于 4KB 容易 I/O 过载,大于 1MB 对多数 SSD 已无明显收益 - 用
f.read(chunk_size)循环读取,返回空字符串''表示 EOF - 文本处理时需自行处理行边界断裂问题:最后一块末尾可能不是完整行,得缓存并拼接到下一块开头
- 二进制场景可直接处理,比如校验哈希、提取 header、流式解压
什么时候该考虑 mmap
mmap 不是“更高级的读文件方式”,它是为特定需求设计的:需要频繁随机访问大文件中的不同位置(比如搜索关键词、修改某偏移处字节),且文件远大于物理内存。
但它有硬门槛:
- 不适用于 Windows 下的只读文本流处理(
mmap映射后仍要自己 decode,且换行符逻辑复杂) - 无法自动处理编码(
mmap操作的是字节,utf-8多字节字符可能被截断) - 映射失败时抛
ValueError或OSError,需捕获;且mmap.mmap(f.fileno(), 0)中的0表示映射全部,对 TB 级文件可能失败 - 真正省内存的前提是:你只访问映射区的一小部分。如果遍历整个
mmap对象,效果未必比for line in f好
绝大多数日志分析、CSV 解析、逐行过滤任务,mmap 是过度设计——它解决的是“如何像操作内存数组一样访问文件某段”,而不是“怎么不爆内存”。

















