土法炼钢兴趣小组的算法知识备份

Python 垃圾回收机制详解

目录

引言

Python (CPython) 的内存管理策略与 Java 和 Go 有显著不同。它主要依赖引用计数 (Reference Counting) 来进行内存回收,辅以一个分代循环垃圾收集器 (Generational Cyclic GC) 来解决引用循环问题。

1. 引用计数 (Reference Counting)

这是 Python 内存管理的基石。每个 Python 对象都有一个 ob_refcnt 字段。

1.1 机制详解

伪代码 (C 语言风格):

#define Py_INCREF(op) (op->ob_refcnt++)

#define Py_DECREF(op) \
    if (--(op->ob_refcnt) == 0) \
        _Py_Dealloc(op)

1.2 优缺点

2. 循环引用与分代收集

为了解决 A -> B -> A 这种循环引用导致内存无法释放的问题,Python 引入了分代循环 GC。

Python Reference Cycle

2.1 容器对象 (Container Objects)

只有可能包含引用的对象(如 list, dict, class, tuple)才会被 GC 跟踪。简单的原子类型(如 int, str)不需要 GC 跟踪,因为它们不可能构成环。

2.2 循环检测算法

Python 使用一种基于引用计数副本的算法来检测环。

算法步骤 (伪代码):

def collect(generation):
    # 1. 建立引用计数副本
    # gc_refs = ob_refcnt
    update_gc_refs() 
    
    # 2. 减去内部引用
    # 遍历该代中所有对象,将其引用的对象的 gc_refs 减 1
    # 这一步是为了消除"环"内部的引用影响
    subtract_refs()
    
    # 3. 标记可达对象
    # 此时,如果 gc_refs > 0,说明有外部引用指向它,它是活跃的。
    # 将其标记为 reachable,并递归标记其引用的对象。
    move_unreachable_to_garbage()
    
    # 4. 回收垃圾
    # 剩下的 gc_refs == 0 的对象就是不可达的环
    delete_garbage()

2.3 分代假设 (Generational Hypothesis)

Python 将对象分为三代:0 代、1 代、2 代。 * 0 代: 新创建的对象。 * 1 代: 幸存过一次 0 代回收的对象。 * 2 代: 幸存过一次 1 代回收的对象。

触发机制: * 当 0 代对象数量减去回收数量达到阈值(默认 700),触发 0 代 GC。 * 当 0 代 GC 发生一定次数(默认 10 次),触发 1 代 GC。 * 以此类推。

3. 性能调优

Python 提供了 gc 模块来控制垃圾回收。

总结

Python 的 GC 是 引用计数 (主) + 分代循环 GC (辅)。 * 引用计数保证了大多数对象的实时回收。 * 分代 GC 兜底解决循环引用,避免内存泄漏。


By .