Python 垃圾回收机制是解释器自动管理内存的核心模块,主要负责识别程序里不再被使用的对象,回收对应的内存空间,避免内存持续占用造成内存溢出。它并不是单一算法,而是由引用计数为主,标记清除和分代回收为辅的组合方案,引用计数负责实时回收大部分对象,标记清除专门解决循环引用无法释放的问题,分代回收用来降低全量扫描带来的性能损耗。之所以需要这套组合机制,是因为 Python 属于高级解释型语言,开发者不需要手动调用 free 释放内存,但是单纯引用计数算法有明显短板,遇到两个对象互相引用的时候,引用计数永远不会降到零,对象就一直无法释放,长期运行的服务就会出现内存泄漏,所以 Python 才额外增加另外两套回收算法做补充,兼顾实时性和处理循环引用的能力。
核心基础 引用计数
每一个 Python 对象内部都保存一个计数字段,记录当前这个对象被多少变量 容器元素 函数参数引用。只要有新引用指向这个对象,计数加一,引用被删除或者变量重新赋值,计数减一。当计数变成 0 的时候,解释器立刻触发销毁,释放这块内存。
优点是响应实时,回收动作马上执行,实现逻辑简单。
缺点是维护计数本身有额外开销,最大缺陷就是无法处理循环引用,也就是 A 引用 B,B 又引用 A,哪怕外部已经没有任何变量指向 A 和 B,两者计数依然大于 0,对象不会自动回收。
标记清除算法
标记清除专门处理循环引用,只针对容器类对象生效,比如列表 字典 实例对象 元组这类可以包含其他对象引用的类型,整数 字符串这类基础不可变对象不参与标记清除。
执行流程分为两个阶段,第一阶段标记,从根对象集合出发,沿着引用链遍历所有可达对象,把可达对象标记成存活。第二阶段清除,遍历之后没有被标记的对象,判定为不可达对象,如果存在循环引用就直接销毁回收内存。
标记清除不是一直运行,是在满足触发条件的时候才启动扫描,减少频繁扫描带来的 CPU 消耗。
分代回收机制
分代回收是在标记清除基础上做性能优化,核心思路是根据对象存活时间划分不同代,Python 一共分为三代,编号 0 代 1 代 2 代。新创建对象默认放在 0 代,对象熬过一次垃圾回收,就晋升到下一代。存活时间越久的对象,被扫描的频率越低。
原理依据是程序里大部分对象生命周期很短,刚创建很快就销毁,长期存活的对象大概率还会继续使用,不需要反复扫描。每一代都设置独立的阈值,当这一代新增对象数量减去回收对象数量超过阈值,就触发对应代的垃圾回收。0 代扫描最频繁,2 代扫描频率最低,大幅降低长时间运行项目的扫描开销。
垃圾回收触发时机
第一种 引用计数归零,立刻自动回收,不需要等待分代回收。
第二种 分代回收达到阈值,自动触发标记清除。
第三种 手动调用 gc 模块的 collect 函数,主动强制执行垃圾回收,适合大批量临时对象创建之后,手动释放内存。
第四种 解释器退出的时候,执行全局清理,回收剩余所有对象。
gc 模块常用操作表格
| 操作 | 作用 | 适用场景 |
|---|---|---|
| gc.enable | 开启分代垃圾回收 | 默认开启,被关闭之后重新启用 |
| gc.disable | 关闭分代垃圾回收 | 对性能要求极高,确认没有循环引用的程序 |
| gc.collect | 手动触发垃圾回收 | 批量生成对象之后,主动释放循环引用内存 |
| gc.get_threshold | 查看三代回收阈值 | 调优回收触发频率 |
| gc.set_debug | 开启回收调试日志 | 排查内存泄漏,观察回收对象 |
实践注意事项
开启分代回收虽然解决循环引用,但是扫描过程会短暂占用 CPU,长时间运行的后端服务如果频繁触发,会出现偶发卡顿。可以通过调整三代阈值,或者在业务低峰期手动执行 gc.collect。如果业务确定不存在循环引用,也可以关闭分代回收,只保留引用计数。还要区分内存泄漏和正常缓存,很多项目会主动保存对象做缓存,这类对象引用计数一直大于 0,不属于垃圾,垃圾回收机制不会回收,不要误判成回收失效。
【常见问题】
问题 1 Python 垃圾回收机制为什么不能只使用引用计数
回答 1 Python 垃圾回收机制如果只使用引用计数,遇到对象之间循环引用的时候,引用计数永远无法降到零,对象内存一直无法释放,长期运行就会产生内存泄漏,所以才增加标记清除和分代回收作为补充方案。
问题 2 Python 垃圾回收机制里面分代回收扫描哪些对象
回答 2 Python 垃圾回收机制的分代回收依托标记清除逻辑,只扫描列表字典类实例这类容器对象,整数字符串这类基础不可变对象不参与分代回收扫描。
问题 3 Python 垃圾回收机制手动调用 gc.collect 有什么副作用
回答 3 Python 垃圾回收机制手动调用 gc.collect 会立刻执行标记清除扫描,扫描过程占用 CPU 资源,如果在高并发业务高峰期调用,容易造成业务响应延迟,一般推荐放在业务空闲时段执行。
问题 4 Python 垃圾回收机制关闭之后是不是就不会回收内存
回答 4 Python 垃圾回收机制关闭的只是分代回收也就是标记清除模块,引用计数仍然正常工作,引用计数归零的对象依旧会立刻释放内存,只是循环引用的对象不再自动回收。
【免责申明】
本文内容仅作为 Python 垃圾回收机制详解的技术参考,不同 Python 版本底层实现细节存在差异,调优垃圾回收参数需要结合项目实际压测验证,本文不保证所有业务场景完全适配,修改回收策略造成的性能波动或者内存异常需要自行承担相关风险。


