Python 的垃圾回收(Garbage Collection,GC)机制主要包括引用计数、标记 - 清除、分代回收这三部分,以下是详细介绍:
引用计数
引用计数是Python最基本的垃圾回收方式,Python为每个对象维护一个引用计数,记录对象被引用的次数。
- 当一个对象被创建并赋值给一个变量时,对象的引用计数加1,如
a = [1, 2, 3],列表 [1, 2, 3] 的引用计数变为1。 - 当对象被其他变量引用时,引用计数再加1,比如
b = a,此时列表 [1, 2, 3] 的引用计数变为2。 - 当对象的引用被删除(变量被销毁或者变量重新赋值),引用计数减1,例如
del a,列表 [1, 2, 3] 的引用计数变为1 。 - 当对象的引用计数变为0时,Python解释器会立即回收该对象所占用的内存空间。
- 优点
- 实时性能即时回收不再使用的对象,内存管理的响应速度快,不会造成大量内存碎片。
- 简单高效实现原理简单,对大多数简单的对象回收场景,开销较小。
- 缺点
- 无法处理循环引用当对象之间存在循环引用时,尽管从外部来看这些对象已经不再被使用,但引用计数永远不会变为0,导致内存泄漏。例如两个对象互相引用:
classNode:def__init__(self):self.next = Nonea = Node()b = Node()a.next = bb.next = adel adel b
这里a和b形成循环引用,即使外部不再有对它们的引用,它们占用的内存也无法通过引用计数回收。
标记 - 清除
标记 - 清除算法是为了解决引用计数无法处理循环引用问题而引入的。
- 标记阶段从一组被称为“根对象”(GC Roots)的对象集合开始,遍历整个对象图,标记所有从根对象可达的对象。根对象包括全局变量、函数栈中的局部变量等。
- 清除阶段遍历整个堆内存,回收所有未被标记的对象,因为这些未被标记的对象意味着从根对象无法访问到,也就是不再被使用。
- 优点
- 缺点
- 暂停时间在标记和清除过程中,需要暂停程序的执行,可能会对程序的实时性产生一定影响。
- 扫描开销每次回收都需要遍历整个堆内存,对于大型程序来说,这会带来一定的性能开销。
分代回收
分代回收是基于对象存活时间的一种优化策略,Python将对象分为0代、1代、2代三个“代”。
- 对象分代新创建的对象都放在0代,当0代对象数量达到一定阈值(默认为700),会触发0代的垃圾回收,回收短命对象。存活下来的对象会被移到1代。
- 代际回收每进行10次0代的垃圾回收,会触发1代的垃圾回收,回收较老的对象,存活下来的对象移到2代。每进行10次1代的垃圾回收,会触发2代的垃圾回收,回收所有对象。因为通常情况下,存活时间越长的对象,越有可能一直被使用,所以2代的扫描频率最低,以此来提高垃圾回收的整体效率。
- 优点
- 提高效率通过对不同代的对象采用不同的回收频率,减少了不必要的扫描,提高了垃圾回收的效率。
- 适应对象生命周期特点符合大多数程序中对象的生命周期模式,新创建的对象往往很快就不再使用,而老对象则更倾向于长期存活。
- 缺点
- 复杂度增加分代回收机制增加了垃圾回收系统的复杂度,需要维护不同代的对象集合和回收策略。
- 参数调整不同的应用场景可能需要调整分代回收的阈值等参数,如果参数设置不当,可能会影响垃圾回收的效果和性能。
流程图
┌───────────────────────────┐ │ 对象创建 (new object) │ └─────────────┬─────────────┘ │ ▼ ┌──────────────────┐ │ 引用计数机制 │ │ (Reference Count)│ └───────┬──────────┘ │ ┌───────────────────┼───────────────────┐ │ │ │ ▼ ▼ ▼引用计数 > 0 引用计数 = 0 循环引用 (A↔B)(继续存活) (立即释放对象) (引用计数无法归零) │ ▼ ┌─────────────────────────┐ │ 标记-清除 (Mark-Sweep) │ │ (检测不可达对象) │ └─────────────┬───────────┘ │ ▼ ┌─────────────────────────┐ │ 分代回收 (Generational)│ │ 三代: 0代/1代/2代 │ └─────────────┬───────────┘ │ ┌─────────────────────────────────┼────────────────────────────────┐ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 0代 (young)│──幸存对象晋升──▶ │ 1代 (middle)│──幸存对象晋升──▶ │ 2代 (old) │ └───────┬─────┘ └───────┬─────┘ └───────┬─────┘ │ │ │阈值触发(默认700) → 回收短命对象 每10次0代GC触发1次 每10次1代GC触发1次 │ │ │ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 0代 GC │ │ 1代 GC │ │ 2代 GC │ │ 快速清理 │ │ 清理更老对象│ │ 全面清理 │ └─────────────┘ └─────────────┘ └─────────────┘
相关的Python标准库函数
在Python中,可以使用gc模块来操作和控制垃圾回收机制:
gc.enable()gc.disable()禁用垃圾回收机制,一般不建议在正常开发中使用,可能会导致内存泄漏。gc.collect([generation])手动触发垃圾回收,可以指定代际,如gc.collect(2) 表示触发2代的垃圾回收。gc.set_threshold(threshold0[, threshold1[, threshold2]])设置分代回收的阈值,如gc.set_threshold(700, 10, 10) 。