深入解析Python垃圾回收机制:引用计数、标记清除与分代回收

桃夭
桃夭
发布时间:2026-10-14| 3人浏览过
原创

本文详细解析Python的垃圾回收(GC)机制,涵盖以引用计数为主、分代回收为辅的核心原理。重点阐述引用计数的运作方式及其无法解决循环引用的缺陷,介绍标记清除算法如何处理容器对象的内存回收,并解析分代回收如何通过年轻代、中年代和老年代的划分优化GC效率。

Python垃圾回收机制概述

一、引用计数

引用计数原理与优缺点

Python 的垃圾回收机制主要依赖于引用计数,并辅以分代回收策略。引用计数法的核心原理是为每个对象维护一个名为 ob_refcnt 的字段,用于记录当前指向该对象的引用数量。当出现以下四种情况之一时,对象的引用计数会加 1:

  • 对象被创建,例如执行赋值语句 a = 14;
  • 对象被其他变量引用,例如执行 b = a;
  • 对象作为参数传递给函数,例如调用 func(a);
  • 对象被放入容器结构中,例如 List = {a, 'a', 'b', 2}。

与上述增加引用的情况相对应,当发生以下四种情况时,对象的引用计数会减 1:

对象的别名被显式销毁,例如执行 del a; 对象的别名被重新赋值为新的对象,例如执行 a = 26; 对象离开其作用域,例如函数 func() 执行完毕时,函数内部所有局部变量的引用计数都会减 1; 从容器中删除元素,或者容器本身被销毁。 当对象的引用计数降为 0 时,Python 虚拟机会立即回收该对象占用的内存。

在 Python 中,一切皆对象,其核心数据结构为 Py_Object。所有 Python 对象的头部都包含这一结构:


// object.h
#define PyObject_HEAD   
 _PyObject_HEAD_EXTRA  
 Py_ssize_t ob_refcnt;  
 struct _typeobject *ob_type;
​
typedef struct _object {
 PyObject_HEAD
} PyObject;

以 int 类型为例,其定义如下:


// intobj.h
typedef struct {
 PyObject_HEAD
 long ob_ival;
} PyIntObject;

简而言之,PyObject 是每个对象必须具备的基础内容,其中的 ob_refcnt 字段负责记录引用计数。每当对象获得新的引用时,ob_refcnt 增加;当对象的引用被删除时,ob_refcnt 减少。一旦引用计数变为 0,该对象的生命周期即告结束。


// object.h
#define Py_INCREF(op) (    
 _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA 
 ((PyObject*)(op))->ob_refcnt++)
​
#define Py_DECREF(op)     
 do {      
 if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA 
 --((PyObject*)(op))->ob_refcnt != 0)  
  _Py_CHECK_REFCNT(op)   
 else      
 _Py_Dealloc((PyObject *)(op));   
 } while (0)

引用计数机制具有明显的优势:

  • 高效性;
  • 运行期无停顿,具备实时性:对象一旦失去所有引用,会立即被释放。实时性还带来一个额外好处,即回收内存的时间分摊到了平时的引用操作中;
  • 对象拥有确定的生命周期;
  • 实现简单易懂。

然而,原始的引用计数法也存在明显的缺点:

  • 维护引用计数需要消耗系统资源,维护成本与引用赋值的次数成正比;
  • 无法解决循环引用问题。

例如:


list1 = []
list2 = []
list1.append(list2)
list2.append(list1)

标记清除算法解析

为了解决上述两个致命弱点,Python 引入了另外两种 GC 机制。

二、标记-清除

『标记-清除(Mark-Sweep)』算法是一种基于追踪回收(tracing GC)技术的垃圾回收算法。它包含两个阶段:第一阶段是标记阶段,GC 会给所有『活动对象』打上标记;第二阶段是清除阶段,回收那些没有被标记的『非活动对象』。那么,GC 如何判断哪些是活动对象、哪些是非活动对象呢?

对象之间通过引用(指针)连接,构成一个有向图。对象是有向图的顶点,引用关系是有向图的弧。从根对象(root object)出发,遍历有向图,将可达(reachable)的对象标记为活动对象,不可达的对象则被视为非活动对象。根对象通常包括全局变量、调用栈和寄存器。

在上图中,将小黑圈视为全局变量,即作为 root object。从小黑圈出发,对象 1 可直接到达,因此被标记;对象 2、3 可间接到达,也会被标记;而对象 4 和 5 不可达,因此 1、2、3 是活动对象,4 和 5 是非活动对象,会被 GC 回收。

标记清除算法作为 Python 的辅助垃圾回收技术,主要用于处理容器对象,例如 list、dict、tuple、instance 等,因为字符串、数值等原子类型的对象不可能造成循环引用问题。Python 使用双向链表将容器对象组织起来。不过,这种简单粗暴的标记清除算法也有明显的缺点:在清除非活动对象前,必须顺序扫描整个堆内存,哪怕只剩下小部分非活动对象,也要扫描所有对象。

分代回收策略

三、分代回收

分代回收是一种以空间换时间的操作方式。Python 根据对象的存活时间将内存划分为不同的集合,每个集合称为一个代。Python 将内存分为 3 代,分别为年轻代(第 0 代)、中年代(第 1 代)、老年代(第 2 代),它们对应 3 个链表。垃圾回收频率随着对象存活时间的增大而减小。新创建的对象都会被分配到年轻代,当年轻代链表的节点总数达到上限时,Python 垃圾收集机制就会被触发,把可以被回收的对象回收掉,而不能被回收的对象会被移到中年代去。依此类推,老年代中的对象是存活时间最久的对象,甚至存活于整个系统的生命周期内。分代回收建立在标记清除的基础之上,同样作为 Python 处理容器对象的辅助垃圾回收技术。

以上就是 python 中的 GC 机制的详细内容,更多关于 python GC 的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。