支持循环垃圾回收 — Python 文档
来自菜鸟教程
Python/docs/3.8/c-api/gcsupport
支持循环垃圾回收
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
。 op 还不能被收集器跟踪。
- void PyObject_GC_Track(PyObject *op)
- 将对象 op 添加到收集器跟踪的容器对象集中。 收集器可以在意外的时间运行,因此对象在被跟踪时必须是有效的。 一旦 tp_traverse 处理程序后面的所有字段都有效,通常在构造函数的末尾附近,应该调用它。
同样,对象的解除分配器必须符合一对类似的规则:
- 在引用其他容器的字段无效之前,必须调用 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_dealloc 处理程序)应该在 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 处理程序必须具有以下类型:
- 容器对象的遍历函数。 实现必须为 self 直接包含的每个对象调用 visit 函数,visit 的参数是包含的对象,arg传递给处理程序的值。 不得使用
NULL
对象参数调用 visit 函数。 如果 visit 返回非零值,则应立即返回该值。
为了简化编写 tp_traverse 处理程序,提供了一个 Py_VISIT() 宏。 为了使用这个宏,tp_traverse 实现必须准确地命名它的参数 visit 和 arg:
- void Py_VISIT(PyObject *o)
如果 o 不是
NULL
,则调用 visit 回调,参数为 o 和 arg。 如果 visit 返回一个非零值,则返回它。 使用这个宏, 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())。 如果收集器检测到此对象涉及引用循环,则它会调用此方法。