使对象类型支持循环垃圾回收¶
Python 对循环引用的垃圾检测与回收需要“容器”对象类型的支持,此类型的容器对象中可能包含其它容器对象。不保存其它对象的引用的类型,或者只保存原子类型(如数字或字符串)的引用的类型,不需要显式提供垃圾回收的支持。
要创建一个容器类,类型对象的 tp_flags 字段必须包括 Py_TPFLAGS_HAVE_GC 并提供一个 tp_traverse 处理器的实现。如果该类型的实例是可变的,则还必须提供 tp_clear 的实现。
Py_TPFLAGS_HAVE_GC设置了此标志位的类型的对象必须符合此处记录的规则。为方便起见,下文把这些对象称为容器对象。
容器类型的构造函数必须符合两个规则:
该对象的内存必须使用
PyObject_GC_New或PyObject_GC_NewVar来分配。初始化了所有可能包含其他容器的引用的字段后,它必须调用
PyObject_GC_Track()。
同样的,对象的释放器必须符合两个类似的规则:
在引用其它容器的字段失效前,必须调用
PyObject_GC_UnTrack()。必须使用
PyObject_GC_Del()释放对象的内存。警告
如果一个类型添加了 Py_TPFLAGS_HAVE_GC,则它 必须 实现至少一个
tp_traverse句柄或显式地使用来自其一个或多个子类的句柄。当调用
PyType_Ready()或者某些间接调用该函数的 API 如PyType_FromSpecWithBases()或PyType_FromSpec()时解释器将自动填充tp_flags,tp_traverse和tp_clear字段,如果该类型是继承自实现了垃圾回收器协议的类并且该子类 没有 包括Py_TPFLAGS_HAVE_GC旗标的话。
-
PyObject_GC_New(TYPE, typeobj)¶
类似于
PyObject_New但专用于设置了Py_TPFLAGS_HAVE_GC旗标的容器对象。请不要直接调用此函数为对象分配内存;而应调用类型的
tp_alloc槽位。在填充类型的
tp_alloc槽位时,推荐使用PyType_GenericAlloc()而不是使用简单调用此宏的自定义函数。由此宏分配的内存必须用
PyObject_GC_Del()来释放(通常是通过对象的tp_free槽位来调用)。
-
PyObject_GC_NewVar(TYPE, typeobj, size)¶
与
PyObject_NewVar类似但专用于设置了Py_TPFLAGS_HAVE_GC旗标的容器对象。请不要直接调用此函数为对象分配内存;而应调用类型的
tp_alloc槽位。在填充类型的
tp_alloc槽位时,推荐使用PyType_GenericAlloc()而不是使用简单调用此宏的自定义函数。由此宏分配的内存必须用
PyObject_GC_Del()来释放(通常是通过对象的tp_free槽位来调用)。
-
PyObject *PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *type, size_t extra_size)¶
- 这是 不稳定 API。它可能在次要版本中不经警告地被更改。
与
PyObject_GC_New类似但会在对象的末尾分配 extra_size 个字节(在tp_basicsize偏移量处)。除Python 对象标头外,分配的内存将初始化为零。附加数据将与对象一起被释放,但在其他情况下则不会由 Python 来管理。
由此函数分配的内存必须用
PyObject_GC_Del()来释放(通常是通过对象的tp_free槽位来调用)。警告
此函数被标记为非稳定的因为在实例之后保留附加数据的机制尚未确定。要分配可变数量的字段,推荐改用
PyVarObject和tp_itemsize字段。Added in version 3.12.
-
PyObject_GC_Resize(TYPE, op, newsize)¶
重新调整
PyObject_NewVar所分配对象的大小。返回调整大小后的类型为TYPE*的对象(指向任意 C 类型)或在失败时返回NULL。op 必须为 PyVarObject* 类型并且不能已被回收器所追踪。 newsize 必须为
Py_ssize_t类型。
-
void PyObject_GC_Track(PyObject *op)¶
- 属于 稳定 ABI.
把对象 op 加入到垃圾回收器跟踪的容器对象中。对象在被回收器跟踪时必须保持有效的,因为回收器可能在任何时候开始运行。在
tp_traverse处理前的所有字段变为有效后,必须调用此函数,通常在靠近构造函数末尾的位置。
-
int PyObject_GC_IsTracked(PyObject *op)¶
- 属于 稳定 ABI 自 3.9 版起.
如果 op 对象的类型实现了 GC 协议且 op 目前正被垃圾回收器追踪则返回 1,否则返回 0。
这类似于 Python 函数
gc.is_tracked()。Added in version 3.9.
-
int PyObject_GC_IsFinalized(PyObject *op)¶
- 属于 稳定 ABI 自 3.9 版起.
如果 op 对象的类型实现了 GC 协议且 op 已经被垃圾回收器终结则返回 1,否则返回 0。
这类似于 Python 函数
gc.is_finalized()。Added in version 3.9.
-
void PyObject_GC_Del(void *op)¶
- 属于 稳定 ABI.
释放之前使用
PyObject_GC_New或PyObject_GC_NewVar分配给对象的内存。请不要直接调用此函数来释放对象的内存;而应调用类型的
tp_free槽位。请不要为由
PyObject_New,PyObject_NewVar或相关联的分配函数分配的内存使用此宏;而应改用PyObject_Free()。参见
PyObject_Free()是此函数的非 GC 对应物。
-
void PyObject_GC_UnTrack(void *op)¶
- 属于 稳定 ABI.
从回收器跟踪的容器对象集合中移除 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)¶
- 属于 稳定 ABI.
传给
tp_traverse处理的访问函数的类型。object 是容器中需要被遍历的一个对象,第三个形参对应于tp_traverse处理的 arg 。Python 核心使用多个访问者函数实现循环引用的垃圾检测,不需要用户自行实现访问者函数。
tp_clear 处理程序必须为 inquiry 类型,如果对象不可变则为 NULL 值。
-
typedef int (*inquiry)(PyObject *self)¶
- 属于 稳定 ABI.
丢弃产生循环引用的引用。不可变对象不需要声明此方法,因为它们不可能直接产生循环引用。需要注意的是,对象在调用此方法后必须仍是有效的(不能对引用只调用
Py_DECREF()方法)。当垃圾回收器检测到该对象在循环引用中时,此方法会被调用。
Traversal¶
tp_traverse 处理必须是以下类型:
-
typedef int (*traverseproc)(PyObject *self, visitproc visit, void *arg)¶
- 属于 稳定 ABI.
Traversal function for a garbage-collected object, used by the garbage collector to detect reference cycles. Implementations must call the visit function for each object directly contained by self, with the parameters to visit being the contained object and the arg value passed to the handler. The visit function must not be called with a
NULLobject argument. If visit returns a non-zero value, that value should be returned immediately.A typical
tp_traversefunction calls thePy_VISIT()convenience macro on each of the instance's members that are Python objects that the instance owns. For example, this is a (slightly outdated) traversal function for thethreading.localclass:static int local_traverse(PyObject *op, visitproc visit, void *arg) { localobject *self = (localobject *) op; Py_VISIT(Py_TYPE(self)); Py_VISIT(self->args); Py_VISIT(self->kw); Py_VISIT(self->dict); return 0; }
备注
Py_VISIT()requires the visit and arg parameters tolocal_traverse()to have these specific names; don't name them just anything.Instances of heap-allocated types hold a reference to their type. Their traversal function must therefore visit the type:
Py_VISIT(Py_TYPE(self));
Alternately, the type may delegate this responsibility by calling
tp_traverseof a heap-allocated superclass (or another heap-allocated type, if applicable). If they do not, the type object may not be garbage-collected.If the
Py_TPFLAGS_MANAGED_DICTbit is set in thetp_flagsfield, the traverse function must callPyObject_VisitManagedDict()like this:int err = PyObject_VisitManagedDict((PyObject*)self, visit, arg); if (err) { return err; }
Only the members that the instance owns (by having strong references to them) must be visited. For instance, if an object supports weak references via the
tp_weaklistslot, the pointer supporting the linked list (what tp_weaklist points to) must not be visited as the instance does not directly own the weak references to itself.The traversal function has a limitation:
警告
The traversal function must not have any side effects. Implementations may not modify the reference counts of any Python objects nor create or destroy any Python objects, directly or indirectly.
This means that most Python C API functions may not be used, since they can raise a new exception, return a new reference to a result object, have internal logic that uses side effects. Also, unless documented otherwise, functions that happen to not have side effects may start having them in future versions, without warning.
For a list of safe functions, see a separate section below.
备注
The
Py_VISIT()call may be skipped for those members that provably cannot participate in reference cycles. In thelocal_traverseexample above, there is also aself->keymember, but it can only beNULLor a Python string and therefore cannot be part of a reference cycle.On the other hand, even if you know a member can never be part of a cycle, as a debugging aid you may want to visit it anyway just so the
gcmodule'sget_referents()function will include it.备注
The
tp_traversefunction can be called from any thread.CPython 实现细节: Garbage collection is a "stop-the-world" operation: even in free threading builds, only one thread state is attached when
tp_traversehandlers run.在 3.9 版本发生变更: Heap-allocated types are expected to visit
Py_TYPE(self)intp_traverse. In earlier versions of Python, due to bug 40217, doing this may lead to crashes in subclasses.
To simplify writing tp_traverse handlers,
a Py_VISIT() macro is provided.
In order to use this macro, the tp_traverse
implementation must name its arguments exactly visit and arg:
-
Py_VISIT(o)¶
If the PyObject* o is not
NULL, call the visit callback, with arguments o and arg. If visit returns a non-zero value, then return it.This corresponds roughly to:
#define Py_VISIT(o) \ if (op) { \ int visit_result = visit(o, arg); \ if (visit_result != 0) { \ return visit_result; \ } \ }
Traversal-safe functions¶
The following functions and macros are safe to use in a
tp_traverse handler:
the visit function passed to
tp_traversePy_TYPE(): if called from atp_traversehandler,Py_TYPE()'s result will be valid for the duration of the handler callPyObject_TypeCheck(),PyType_IsSubtype(),PyType_HasFeature()Py<type>_CheckandPy<type>_CheckExact-- for example,PyTuple_Check()
"DuringGC" functions¶
The following functions should only be used in a
tp_traverse handler; calling them in other
contexts may have unintended consequences.
These functions act like their counterparts without the _DuringGC suffix,
but they are guaranteed to not have side effects, they do not set an exception
on failure, and they return/set borrowed references
as detailed in the individual documentation.
Note that these functions may fail (return NULL or -1),
but as they do not set an exception, no error information is available.
In some cases, failure is not distinguishable from a successful NULL result.
-
void *PyObject_GetTypeData_DuringGC(PyObject *o, PyTypeObject *cls)¶
-
void *PyObject_GetItemData_DuringGC(PyObject *o)¶
-
void *PyType_GetModuleState_DuringGC(PyTypeObject *type)¶
-
void *PyModule_GetState_DuringGC(PyObject *module)¶
-
int PyModule_GetToken_DuringGC(PyObject *module, void **result)¶
- 属于 稳定 ABI 自 3.15 版起.
See "DuringGC" functions for common information.
Added in version 3.15.0a8 (unreleased).
-
int PyType_GetBaseByToken_DuringGC(PyTypeObject *type, void *tp_token, PyTypeObject **result)¶
- 属于 稳定 ABI 自 3.15 版起.
See "DuringGC" functions for common information.
Sets *result to a borrowed reference rather than a strong one. The reference is valid for the duration of the
tp_traversehandler call.Added in version 3.15.0a8 (unreleased).
-
PyObject *PyType_GetModule_DuringGC(PyTypeObject *type)¶
-
PyObject *PyType_GetModuleByToken_DuringGC(PyTypeObject *type, const void *mod_token)¶
- 返回值:借入的引用。 属于 稳定 ABI 自 3.15 版起.
See "DuringGC" functions for common information.
These functions return a borrowed reference, which is valid for the duration of the
tp_traversehandler call.Added in version 3.15.0a8 (unreleased).
控制垃圾回收器状态¶
这个 C-API 提供了以下函数用于控制垃圾回收的运行。
-
Py_ssize_t PyGC_Collect(void)¶
- 属于 稳定 ABI.
执行完全的垃圾回收,如果垃圾回收器已启用的话。 (请注意
gc.collect()会无条件地执行它。)返回已回收的 + 无法回收的不可获取对象的数量。如果垃圾回收器被禁用或已在执行回收,则立即返回
0。在垃圾回收期间发生的错误会被传给sys.unraisablehook。此函数不会引发异常。
-
int PyGC_Enable(void)¶
- 属于 稳定 ABI 自 3.10 版起.
启用垃圾回收器:类似于
gc.enable()。返回之前的状态,0 为禁用而 1 为启用。Added in version 3.10.
-
int PyGC_Disable(void)¶
- 属于 稳定 ABI 自 3.10 版起.
禁用垃圾回收器:类似于
gc.disable()。返回之前的状态,0 为禁用而 1 为启用。Added in version 3.10.
-
int PyGC_IsEnabled(void)¶
- 属于 稳定 ABI 自 3.10 版起.
查询垃圾回收器的状态:类似于
gc.isenabled()。返回当前的状态,0 为禁用而 1 为启用。Added in version 3.10.
查询垃圾回收器状态¶
该 C-API 提供了以下接口用于查询有关垃圾回收器的信息。
-
void PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg)¶
- 这是 不稳定 API。它可能在次要版本中不经警告地被更改。
在全部活动的支持 GC 的对象上运行所提供的 callback。 arg 会被传递给所有 callback 的调用。
警告
如果新对象被回调所(取消)分配,则它们是否会被访问是未定义的。
垃圾回收在运行期间被禁用。在回调中显式地运行回收可能导致未定义的行为,例如多次访问同一对象或完全不访问。
Added in version 3.12.
-
typedef int (*gcvisitobjects_t)(PyObject *object, void *arg)¶
要传给
PyUnstable_GC_VisitObjects()的访问者函数的类型。 arg 与传给PyUnstable_GC_VisitObjects的 arg 相同。返回1以继续迭代,返回0以停止迭代。 其他返回值目前被保留因此返回任何其他值的行为都是未定义的。Added in version 3.12.