Python读取大文件内存溢出的根本原因是readlines()、read()、pd.read_excel()等默认全量加载,导致内存占用达文件体积2–4倍;应改用生成器分块读取,如itertools.islice按行分块或二进制read()按字节分块,并慎用openpyxl流式读Excel。

Python读取大文件时内存溢出,根本不是代码写错了,而是默认行为把整个文件塞进内存——readlines()、read()、pd.read_excel() 都是这么干的。10GB 日志、300MB Excel、上亿行 CSV,只要一次性加载,就大概率触发 MemoryError 或被系统 OOM Killer 杀掉。
为什么 readlines() 和 read() 会直接爆内存
这两个方法不区分文件大小,一律全量载入:前者生成一个包含全部行字符串的 list,后者返回一个超长 str 对象。Python 的对象头、引用计数、字符串缓冲区叠加起来,实际内存占用常是文件体积的 2–4 倍。
- 常见错误现象:
MemoryError、ps aux显示 Python 进程 RSS 占用飙升到数 GB、系统卡顿甚至假死 - 适用场景误判:以为“只是读个文件”,没意识到日志/CSV/JSONL 文件天然可能达 GB 级
- 关键陷阱:哪怕你只想要第 100 万行,
readlines()[999999]也得先把前面 999999 行全 load 进内存
用 yield 写一个真正可控的分块生成器(文本行级)
核心是放弃“全量构造”,改用“按需产出”。itertools.islice 配合文件对象迭代器最稳,避免手动计数越界或漏行。
from itertools import islice <p>def chunked_lines(filename, chunk_size=10000): with open(filename, 'r', encoding='utf-8') as f: while True: chunk = list(islice(f, chunk_size)) if not chunk: break yield chunk </p>
- 每次只 hold 住
chunk_size行,处理完立刻丢弃,内存恒定在单块水平 - 别在循环里做
all_lines += chunk或list(chunked_lines(...))——这等于又全量加载了 - 块大小建议从 1k–10k 行起步;若单行极长(如 JSONL),改用字节块更稳妥(见下一条)
二进制/超长行场景:用固定字节块 + read() 分块
逐行读在超长行(如单行几 MB 的 base64 字段)、无换行结构(日志混排、二进制嵌套)时失效,此时必须按字节切块。
def read_in_chunks(file_path, chunk_size=1024*1024): # 默认 1MB
with open(file_path, 'rb') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
yield chunk
- 适用于:.bin、.log(无规范换行)、.xlsx 解压流、网络响应体等
-
chunk_size不是越大越好:1MB 是平衡 I/O 效率和内存占用的常用起点;SSD 上可试 4MB,HDD 上 512KB 更稳 - 注意编码:二进制块需自行解码(如
chunk.decode('utf-8', errors='ignore')),别直接当文本传给str方法
Excel 大文件不能用 pd.read_excel()?试试 openpyxl 只读流式
pandas.read_excel() 底层调 openpyxl,但默认禁用流式能力。直接绕过 pandas,用 openpyxl.load_workbook(read_only=True) 才能真正控内存。
from openpyxl import load_workbook
<p>wb = load_workbook('huge.xlsx', read_only=True)
ws = wb.active
for row in ws.iter_rows(values_only=True):</p><h1>每次只产生一行元组,内存不随总行数增长</h1><pre class="brush:php;toolbar:false;">process(row)wb.close()
-
read_only=True关键:禁用样式、公式、DOM 树构建,内存从 GB 级降到 MB 级 -
iter_rows()返回生成器,不是列表;values_only=True避免单元格对象开销 - 如果必须用 pandas,
pd.read_excel(chunksize=...)仅对 .csv/.txt 有效,对 .xlsx 无效——这是很多人踩坑的盲区
生成器本身不解决所有问题:它只保证“不存全量”,但如果你在每块里累积结果、或调用 list() 强转,就前功尽弃。真正的内存控制,始于对数据生命周期的显式管理——哪块该留,哪块该扔,得自己说了算。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











