python垃圾回收是引用计数、分代机制和标记-清除三者协作的分层流程:引用计数实时销毁零引用对象;分代机制按存活时间将对象分为三代并分级扫描;标记-清除专解循环引用,从根集标记可达对象后回收未标记对象。

Python 的垃圾回收不是单一动作,而是一套分层协作的流程:引用计数实时清理、分代机制按对象年龄调度扫描、标记-清除专解循环引用。三者共同确保内存不堆积,也不频繁停顿。
引用计数:最底层的即时回收
每个 Python 对象头部都存有 ob_refcnt 字段,记录当前有多少变量或容器在引用它。只要这个数归零,对象立刻被销毁,不等任何其他机制介入。
以下操作会直接影响计数:
- 赋值(
a = obj)、传参、放入列表/字典 → 计数 +1 -
del a、变量重赋值、函数退出、容器清空 → 计数 −1 -
sys.getrefcount(obj)本身会临时加 1,查看时需注意偏移
优点是响应快、无延迟;缺点是无法处理 a.append(b); b.append(a) 这类互相持有引用的情况——它们的计数永远 ≥1,但实际已脱离程序逻辑。
立即学习“Python免费学习笔记(深入)”;
分代回收:按“存活时间”分级扫描
CPython 将对象划分为三代(0、1、2),依据是“越新的对象越可能很快死亡”。每代维护独立计数器,达到阈值即触发对应层级的扫描:
- 第 0 代:新创建对象,默认阈值 700。每当新分配对象数 − 已回收数 ≥ 700,就扫描 0 代中不可达对象
- 第 1 代:从 0 代幸存下来的对象。每执行 10 次 0 代回收,自动触发一次 1 代回收(扫描 0+1 代)
- 第 2 代:长期存活对象。每执行 10 次 1 代回收,触发一次全量扫描(0+1+2 代)
这种设计大幅减少对老对象的重复检查,兼顾效率与可靠性。你可以用 gc.get_threshold() 查看当前阈值,用 gc.set_threshold(500, 5, 5) 调整频率。
标记-清除:专治循环引用的兜底手段
当分代回收(或手动调用 gc.collect())启动时,GC 进入标记-清除阶段:
- 标记:从根集(如栈帧变量、全局命名空间)出发,递归遍历所有可达对象并打标
- 清除:回收所有未被标记的对象——哪怕它们的引用计数不为 0(比如因循环引用卡在 1)
注意:含 __del__ 方法的对象可能无法被标记-清除回收,因为析构顺序不确定,GC 会跳过它们并放入 gc.garbage 列表供人工干预。
手动触发与调试控制
默认情况下 GC 是启用的,但你可在特定时机主动干预:
-
gc.collect():完整回收,返回本次清理的不可达对象数量 -
gc.collect(0):只清理第 0 代,开销最小,适合高频轻量清理 -
gc.disable():关闭自动 GC(仅限确认无循环引用的极简场景) -
gc.set_debug(gc.DEBUG_STATS):输出每次回收的统计摘要到 stderr -
gc.set_debug(gc.DEBUG_SAVEALL):把无法回收的对象存入gc.garbage,便于排查泄漏
典型适用场景包括:批量数据处理后、长周期服务的定时清理、或怀疑存在隐性循环引用时的诊断阶段。








