Python学习笔记-gc垃圾回收
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,导致内存泄漏。例如两个对象互相引用:
class Node:
def __init__(self):
self.next = None
a = Node()
b = Node()
a.next = b
b.next = a
del a
del 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)
。