Python 2.7 文档的两个部分提到为扩展模块中定义的容器对象添加循环垃圾回收 (CGC) 支持。
Python/C API Reference Manual给出两个规则,即,
- The memory for the object must be allocated using
PyObject_GC_New()
orPyObject_GC_NewVar()
.- Once all the fields which may contain references to other containers are initialized, it must call
PyObject_GC_Track()
.
而在 Extending and Embedding the Python Interpreter ,对于 Noddy
示例,似乎添加 Py_TPFLAGS_HAVE_GC
标志并填充 tp_traverse
和 tp_clear
插槽就足够了启用 CGC 支持。并且上面的两个规则根本没有被实践。
当我修改 Noddy
示例以实际遵循 PyObject_GC_New()
/PyObject_GC_Del()
和 PyObject_Track() 的规则时
/PyObject_GC_UnTrack()
,它令人惊讶地引发了断言错误,
Modules/gcmodule.c:348: visit_decref: Assertion "gc->gc.gc_refs != 0" failed. refcount was too small
这让我对实现 CGC 的正确/安全方式感到困惑。任何人都可以提供建议,或者最好提供一个支持 CGC 的容器对象的简洁示例吗?
最佳答案
在大多数正常情况下,您不需要自己进行跟踪/取消跟踪。文档中对此进行了描述,但并未明确说明。在 Noddy
example 的情况下你绝对不会。
简短的版本是一个 TypeObject 包含两个函数指针:tp_alloc
和 tp_free
。默认情况下,tp_alloc
在创建类时调用所有正确的函数(如果设置了 Py_TPFLAGS_HAVE_GC
)并且 tp_free
在销毁时取消跟踪类。
Noddy documentation says (在本节末尾):
That’s pretty much it. If we had written custom
tp_alloc
ortp_free
slots, we’d need to modify them for cyclic-garbage collection. Most extensions will use the versions automatically provided.
不幸的是,Supporting Cyclic Garbage Collection documentation 中没有明确表示您不需要自己执行此操作的一个地方是.
详细信息:
Noddy 是使用一个名为 Noddy_new
的函数分配的,该函数放置在 TypeObject
的 tp_new
槽中。根据the documentation ,"new"函数应该做的主要事情是调用 tp_alloc
slot .您通常不会自己编写 tp_alloc
,它只是默认为 PyType_GenericAlloc()
。
查看PyType_GenericAlloc()
in the Python source显示了它根据 PyType_IS_GC(type)
更改的许多部分。首先它调用 _PyObject_GC_Malloc
而不是 PyObject_Malloc
,然后调用 _PyObject_GC_TRACK(obj)
。 [请注意,PyObject_New
真正做的是调用 PyObject_Malloc
,然后调用 tp_init
。]
同样,在释放时调用 tp_free
slot ,自动设置为 PyObject_GC_Del
对于具有 Py_TPFLAGS_HAVE_GC
的类。 PyObject_GC_Del
包含与 PyObject_GC_UnTrack
相同的代码,因此无需调用 untrack。
关于python - 更正扩展模块中的循环垃圾收集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12255537/