支持循环垃圾收集

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 失败论。 op 收集器还不能跟踪。

void PyObject_GC_Track(PyObject *op)

添加对象 op 到收集器跟踪的容器对象集。收集器可以在意外的时间运行,因此跟踪对象时对象必须有效。这应该在所有字段后跟 tp_traverse 处理程序变为有效的,通常在构造函数末尾附近。

int PyObject_IS_GC(PyObject *obj)

如果对象实现垃圾收集器协议,则返回非零,否则返回0。

如果此函数返回0,垃圾收集器将无法跟踪该对象。

int PyObject_GC_IsTracked(PyObject *op)

如果对象类型为 op 实现GC协议和 op 垃圾收集器当前正在跟踪,否则为0。

这类似于Python函数 gc.is_tracked() .

3.9 新版功能.

int PyObject_GC_IsFinalized(PyObject *op)

如果对象类型为 op 实现GC协议和 op 已由垃圾收集器完成,否则为0。

这类似于Python函数 gc.is_finalized() .

3.9 新版功能.

同样,对象的DealLocator必须符合类似的一对规则:

  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_dealloc handler)应该在 tp_traverse 处理程序无效。

在 3.8 版更改: 这个 _PyObject_GC_TRACK()_PyObject_GC_UNTRACK() 宏已从公共C API中删除。

这个 tp_traverse 处理程序接受此类型的函数参数:

typedef int (*visitproc)(PyObject *object, void *arg)

传递给 tp_traverse 处理程序。应使用要遍历的对象调用函数 object 第三个参数 tp_traverse 处理程序组件 arg . python核心使用几个访问者函数来实现循环垃圾检测;用户不需要编写自己的访问者函数。

这个 tp_traverse 处理程序必须具有以下类型:

typedef int (*traverseproc)(PyObject *self, visitproc visit, void *arg)

容器对象的遍历函数。实现必须调用 参观 直接包含的每个对象的函数 self ,参数为 参观 作为被包含的对象和 arg 传递给处理程序的值。这个 参观 函数不能用 NULL 对象参数。如果 参观 返回一个非零值,该值应立即返回。

简化写作 tp_traverse 处理程序,A Py_VISIT() 提供宏。要使用此宏,请 tp_traverse 实现必须准确命名其参数 参观arg

void Py_VISIT(PyObject *o)

如果 o 不是 NULL 调用 参观 回调,带参数 oarg . 如果 参观 返回一个非零值,然后返回它。使用这个宏, 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 如果对象是不可变的。

typedef int (*inquiry)(PyObject *self)

删除可能已创建引用循环的引用。不可变对象不必定义此方法,因为它们永远不能直接创建引用循环。请注意,调用此方法后,对象必须仍然有效(不要只调用 Py_DECREF() 参考)。如果收集器检测到该对象涉及引用循环,则将调用此方法。