Generator闭包持有大对象会导致其无法释放,内存持续增长;应改用weakref、参数传递或手动del清理。

Generator 内部闭包持有大对象,会导致这些对象在整个生成器生命周期内无法被释放——哪怕只 yield 一个值,闭包捕获的引用仍牢牢挂在栈帧或生成器状态里,堆内存基线会持续抬高,甚至随迭代次数线性增长。
识别闭包隐式捕获的大对象
常见于工具函数、回调封装或带上下文的生成器中。例如:
def make_data_stream(big_matrix):
# big_matrix 被闭包捕获,整个生成器运行期间它都不可回收
return (row.sum() for row in big_matrix)即使 big_matrix 是 NumPy 数组(几 MB 或更大),它也会因闭包强引用而常驻内存。可用 objgraph.get_referrers(big_matrix) 验证是否被生成器帧持有。
用 weakref 解耦闭包与大对象
不直接引用大对象,改用弱引用 + 显式检查,确保对象可被及时回收:
- 将大对象传入时转为
weakref.ref,在每次yield前检查是否仍存活 - 避免在 lambda 或嵌套函数中直接使用外部大变量
- 示例重构:
import weakref import numpy as np <p>def safe_stream_from_matrix(big_mat): ref = weakref.ref(big_mat) def _gen(): mat = ref() if mat is None: return for row in mat: yield row.sum() return _gen()
改用参数传递替代闭包捕获
把生成器写成显式接收大对象的函数,而非靠作用域自动捕获:
- 定义纯生成器函数:
def data_generator(matrix, start=0): - 调用时传入副本或视图(如
matrix[start:start+batch]),避免持有所有数据 - 配合
.copy()或np.array(..., copy=True)控制所有权边界
手动清理生成器帧中的局部引用
在生成器退出前(如被中断或迭代结束),显式清空帧内大变量:
- 用
sys._getframe(1)获取当前生成器帧(仅限调试/受控环境) - 更稳妥方式:在生成器末尾加
del big_obj,并在外层确保无其他强引用 - 对 NumPy 数组等,额外调用
np.shares_memory()检查是否意外共享底层 buffer
闭包不是语法糖,而是内存契约。Generator 一旦持有了大对象,就等于给它签了长期居住证——要让它搬走,得主动撕毁这份契约。

















