Supporting Cyclic Garbage Collection¶
Python对检测和收集涉及循环引用的垃圾的支持需要来自对象类型的支持,这些对象类型对于也可能是容器的其他对象是“容器”。不存储对其他对象的引用或仅存储对原子类型(例如数字或字符串)的引用的类型不需要为垃圾容器提供任何显式支持。
要创建容器类型,类型对象的tp_flags
字段必须包含Py_TPFLAGS_HAVE_GC
并提供tp_traverse
处理程序的实现。如果类型的实例是可变的,还必须提供tp_clear
实现。
Py_TPFLAGS_HAVE_GC
具有此标志集的类型的对象必须符合此处记录的规则。为了方便起见,这些对象将被称为容器对象。
容器类型的构造函数必须符合两个规则:
- 对象的内存必须使用
PyObject_GC_New()
或PyObject_GC_NewVar()
分配。 - 一旦所有可能包含对其他容器的引用的字段被初始化,它必须调用
PyObject_GC_Track()
。
- TYPE*
PyObject_GC_New
(TYPE, PyTypeObject *type)¶ 类似于
PyObject_New()
,但是对于设置了Py_TPFLAGS_HAVE_GC
标志的容器对象。
- TYPE*
PyObject_GC_NewVar
(TYPE, PyTypeObject *type, Py_ssize_t size)¶ 类似于
PyObject_NewVar()
,但是对于设置了Py_TPFLAGS_HAVE_GC
标志的容器对象。
- TYPE*
PyObject_GC_Resize
(TYPE, PyVarObject *op, Py_ssize_t newsize)¶ 调整由
PyObject_NewVar()
分配的对象的大小。在失败时返回调整大小的对象或NULL。
- void
PyObject_GC_Track
(PyObject *op)¶ 将对象op添加到收集器跟踪的容器对象集合。收集器可以在意外的时间运行,因此对象在被跟踪时必须有效。一旦所有字段后面跟随
tp_traverse
处理程序成为有效,通常在构造函数的结尾附近,这应该被调用。
- void
_PyObject_GC_TRACK
(PyObject *op)¶ PyObject_GC_Track()
的宏版本。它不应用于扩展模块。
类似地,对象的释放器必须符合类似的一对规则:
- 在引用其他容器的字段无效之前,必须调用
PyObject_GC_UnTrack()
。 - 对象的内存必须使用
PyObject_GC_Del()
解除分配。
- void
PyObject_GC_Del
(void *op)¶ 使用
PyObject_GC_New()
或PyObject_GC_NewVar()
释放分配给对象的内存。
- void
PyObject_GC_UnTrack
(void *op)¶ 从收集器跟踪的容器对象集合中删除对象op。请注意,可以再次调用
PyObject_GC_Track()
此对象,将其添加回跟踪对象集。在tp_traverse
处理程序使用的任何字段无效之前,deallocator(tp_dealloc
处理程序)应调用此对象。
- void
_PyObject_GC_UNTRACK
(PyObject *op)¶ PyObject_GC_UnTrack()
的宏版本。它不应用于扩展模块。
tp_traverse
处理程序接受此类型的函数参数:
- int
(*visitproc)
(PyObject *object, void *arg)¶ 传递到
tp_traverse
处理程序的访问者函数的类型。该函数应该作为对象遍历的对象和第三个参数以arg的形式调用到tp_traverse
处理程序。Python核心使用几个访问器函数来实现循环垃圾检测;不希望用户需要编写自己的访问者函数。
tp_traverse
处理程序必须具有以下类型:
- int
(*traverseproc)
(PyObject *self, visitproc visit, void *arg)¶ 容器对象的遍历函数。实现必须为self直接包含的每个对象调用访问函数,参数为访问为包含的对象,arg 传递给处理程序的值。不能使用NULL对象参数调用访问函数。如果访问返回非零值,则应立即返回该值。
为了简化写入tp_traverse
处理程序,提供了一个Py_VISIT()
宏。为了使用此宏,tp_traverse
实施必须完全命名其参数访问和arg:
- void
Py_VISIT
(PyObject *o)¶ 如果o不是NULL,请调用访问回调,参数为o和arg t4 >。如果访问返回非零值,则返回它。使用此宏,
tp_traverse
处理程序看起来像:static int my_traverse(Noddy *self, visitproc visit, void *arg) { Py_VISIT(self->foo); Py_VISIT(self->bar); return 0; }
如果对象是不可变的,则tp_clear
处理程序必须是inquiry
类型,或NULL。
- int
(*inquiry)
(PyObject *self)¶ 删除可能创建了引用循环的引用。不可变对象不必定义此方法,因为它们永远不能直接创建引用循环。注意,在调用此方法后,对象必须仍然有效(不要在引用上调用
Py_DECREF()
)。收集器将调用此方法,如果它检测到此对象参与一个参考循环。