在扩展模块中更正循环垃圾收集

liu*_*uyu 6 c python garbage-collection python-c-api python-extensions

Python 2.7的doc的两个部分提到为扩展模块中定义的容器对象添加循环垃圾收集(CGC)支持.

Python的/ C API参考手册给出了两个规则,即

  1. 必须使用PyObject_GC_New()或分配对象的内存PyObject_GC_NewVar().
  2. 一旦初始化了可能包含对其他容器的引用的所有字段,它就必须调用PyObject_GC_Track().

而在扩展和嵌入Python解释器中,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:断言"gc-> gc.gc_refs!= 0"失败.refcount太小了

这导致我对实现CGC的正确/安全方式感到困惑.任何人都可以给出建议,或者最好是一个有CGC支持的容器对象的简洁例子吗?

Dav*_*idW 4

在大多数正常情况下,您不需要自己进行跟踪/取消跟踪。文档中对此进行了描述,但没有明确说明。在这个例子中Noddy绝对不会。

\n\n

简而言之,TypeObject 包含两个函数指针:tp_alloctp_free。默认情况下,tp_alloc在创建类时调用所有正确的函数(如果Py_TPFLAGS_HAVE_GC已设置),并tp_free在销毁时取消跟踪该类。

\n\n

Noddy文档(在本节的末尾):

\n\n
\n

\xe2\x80\x99 就差不多了。如果我们编写了自定义tp_alloctp_free插槽,我们\xe2\x80\x99d需要修改它们以进行循环垃圾收集。大多数扩展将使用自动提供的版本。

\n
\n\n

不幸的是,有一个地方没有明确表示您不需要自己执行此操作,即支持循环垃圾收集文档

\n\n
\n\n

细节:

\n\n

Noddy 的分配是使用一个名为Noddy_newput 的tp_new函数放入TypeObject. 根据文档,“新”函数应该做的主要事情是调用tp_alloc。您通常不会tp_alloc自己编写,它只是默认为PyType_GenericAlloc().

\n\n

查看PyType_GenericAlloc()Python 源代码会发现它基于PyType_IS_GC(type). 首先它调用_PyObject_GC_Malloc而不是PyObject_Malloc,然后它调用_PyObject_GC_TRACK(obj)。[请注意,真正PyObject_New要做的只是调用PyObject_Malloc然后tp_init。]

\n\n

类似地,在释放时,您调用tp_freeslotPyObject_GC_Del ,对于带有 的类,它会自动设置为Py_TPFLAGS_HAVE_GCPyObject_GC_Del包含与此相同的代码,PyObject_GC_UnTrack因此无需调用 untrack。

\n