首页 文章

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

提问于
浏览
6

Python 2.7的doc的两个部分提到为扩展模块中定义的容器对象添加循环垃圾收集(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_traversetp_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支持的容器对象的简洁例子吗?

2 回答

  • 1

    我自己在C API方面没有足够的经验给你任何建议,但Python容器实现本身有很多例子 .

    就个人而言,我'd start with the tuple implementation first, since it'不可变:Objects/tupleobject.c . 然后转到 dictlistset 实现,以获取有关可变容器的更多说明:

    我不禁注意到整个地方都有 PyObject_GC_New()PyObject_GC_NewVar() 和_570550的调用,并且设置了 Py_TPFLAGS_HAVE_GC .

  • 3

    在大多数正常情况下,你不应该特别明确 . 在Noddy example的情况下,你绝对不会 .

    简短版本是TypeObject包含两个函数指针: tp_alloctp_free . 默认情况下, tp_alloc 在创建类时调用所有正确的函数(如果设置了 Py_TPFLAGS_HAVE_GC ),并且 tp_free 在破坏时调用类 .

    Noddy documentation says(在本节末尾):

    那就是它 . 如果我们编写了自定义tp_alloc或tp_free插槽,我们需要修改它们以进行循环垃圾收集 . 大多数扩展程序将使用自动提供的版本 .

    不幸的是,一个不需要自己做的地方就是Supporting Cyclic Garbage Collection documentation .


    Detail:

    使用名为 Noddy_new 的函数在 TypeObjecttp_new 槽中分配Noddy . 根据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,对于具有 Py_TPFLAGS_HAVE_GC 的类,它会自动设置为PyObject_GC_Del . PyObject_GC_Del 包含与 PyObject_GC_UnTrack 相同的代码,因此无需调用untrack .

相关问题