Python2.7文档的两部分提到了为扩展模块中定义的容器对象添加循环垃圾收集(cgc)支持。
Python/C API Reference Manual给出了两个规则,即,
必须使用PyObject_GC_New()
或PyObject_GC_NewVar()
分配对象的内存。
一旦初始化所有可能包含对其他容器引用的字段,它必须调用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参考!=0“失败。引用计数太小
这导致了我对实现cgc的正确/安全方法的困惑。有谁能给出建议,或者,最好是一个有cgc支持的容器对象的简洁示例?
最佳答案
在大多数情况下,你不需要自己去做追踪/去追踪。这是在文件中描述的,但是没有具体说明。如果是Noddy
example你肯定不会。
简短的版本是,typeobject包含两个函数指针:tp_alloc
和tp_free
。默认情况下,tp_alloc
在创建类时调用所有正确的函数(如果设置了Py_TPFLAGS_HAVE_GC
),而tp_free
在销毁时取消对类的跟踪。
Noddy documentation says(在本节末尾):
差不多就是这样。如果我们已经编写了自定义的tp_alloc
或tp_free
插槽,那么我们需要修改它们以进行循环垃圾收集。大多数扩展将使用自动提供的版本。
不幸的是,一个地方不清楚你不需要自己做这件事就是Supporting Cyclic Garbage Collection documentation。
细节:
noddy的分配使用一个名为Noddy_new
的函数,该函数放在tp_new
的TypeObject
插槽中。根据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
相同的代码,因此不需要调用取消跟踪。