Supporting Cyclic Garbage Collection

Python对检测和收集涉及循环引用的垃圾的支持需要来自对象类型的支持,这些对象类型对于也可能是容器的其他对象是“容器”。不存储对其他对象的引用或仅存储对原子类型(例如数字或字符串)的引用的类型不需要为垃圾容器提供任何显式支持。

要创建容器类型,类型对象的tp_flags字段必须包含Py_TPFLAGS_HAVE_GC并提供tp_traverse处理程序的实现。如果类型的实例是可变的,还必须提供tp_clear实现。

Py_TPFLAGS_HAVE_GC

具有此标志集的类型的对象必须符合此处记录的规则。为了方便起见,这些对象将被称为容器对象。

容器类型的构造函数必须符合两个规则:

  1. 对象的内存必须使用PyObject_GC_New()PyObject_GC_NewVar()分配。
  2. 一旦所有可能包含对其他容器的引用的字段被初始化,它必须调用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()的宏版本。它不应用于扩展模块。

类似地,对象的释放器必须符合类似的一对规则:

  1. 在引用其他容器的字段无效之前,必须调用PyObject_GC_UnTrack()
  2. 对象的内存必须使用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,请调用访问回调,参数为oarg 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())。收集器将调用此方法,如果它检测到此对象参与一个参考循环。