CPython执行mark-sweep标记清除算法后如何识别未标记对象?
你提到的PyObject基础结构体中的ob_refcnt确实是引用计数的核心存储字段:
typedef struct _object { _PyObject_HEAD_EXTRA Py_ssize_t ob_refcnt; struct _typeobject *ob_type; } PyObject;
这一字段仅负责维护对象的显式引用计数,和标记清除流程的标记逻辑是相互独立的,下面直接回答你的两个疑问:
1. CPython是否维护了存储所有对象的全局列表?
CPython不会维护所有Python对象的全局列表,但是会为所有可能产生循环引用的容器类对象(比如列表、字典、集合、自定义类实例等)维护一个全局的双向GC链表。
非容器类型对象(比如整数、字符串、浮点数等)不存在循环引用的可能,不会被串入这个GC链表,也不需要参与标记清除流程,仅靠引用计数就能完成内存回收。
每个加入GC链表的容器对象,头部都会额外包含_PyObject_GC_HEAD结构,里面存储了链表的前后节点指针、以及GC过程需要用到的标记相关字段,基础PyObject结构本身不包含这些GC相关字段。
2. 标记清除算法执行后如何判断未标记(unmarked)对象?
整个判断逻辑和GC链表高度绑定:
- 标记阶段:GC从根对象集合(包括全局命名空间中的对象、调用栈上的变量引用、正在使用的常量等)出发,遍历所有可达的对象,每找到一个可达对象,就把该对象GC头中的标记位置为1,代表该对象存活。
- 清除阶段:GC直接遍历全局容器对象GC链表,逐个检查每个对象的标记位:
- 标记位为1的是存活对象,将标记位重置为0,留待下次GC使用
- 标记位为0的就是unmarked对象,代表没有任何根引用能到达它,属于需要回收的垃圾对象
早期版本的CPython曾用过「临时修改引用计数」的方式来替代单独的标记位,但逻辑本质完全一致,都是先标记所有可达对象,再遍历GC链表筛选未标记的待回收对象。
内容的提问来源于stack exchange,提问作者Евгений Павлов
相关产品推荐
相关产品推荐

