Корректный циклический сбор мусора в модулях расширения
два раздела документа Python 2.7 упомянули добавление циклический сбор мусора (CGC) поддержка контейнерных объектов, определенных в модулях расширения.
на справочное руководство по API Python/C дает два правила, т. е.
- память для объекта должна быть выделена с помощью
PyObject_GC_New()
илиPyObject_GC_NewVar()
.- после инициализации всех полей, которые могут содержать ссылки на другие контейнеры, он должен вызвать
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()
, это удивительно подняло ошибку утверждения говоря:
модули/gcmodule.c: 348: visit_decref: утверждение " gc - >gc.gc_refs != 0" не удалось. счетчик был слишком мал
это приводит к моей путанице о правильном / безопасном способе реализации CGC. Может ли кто-нибудь дать советы или, предпочтительно,опрятный пример объекта контейнера с поддержкой CGC?
2 ответов
в большинстве нормальных обстоятельств вам не нужно делать отслеживание / отследить себя. Это описано в документации, однако это не сделано специально очистить. В случае Noddy
пример определенно нет.
короткая версия заключается в том, что TypeObject содержит два указателя функции: tp_alloc
и tp_free
. По умолчанию tp_alloc
вызывает все правильные функции при создании класса (если Py_TPFLAGS_HAVE_GC
) и tp_free
untracks в класс по разрушению.
на документация Noddy говорит (в конце секции):
это почти все. Если бы мы написали на заказ
tp_alloc
илиtp_free
слоты, нам нужно будет изменить их для циклической сборки мусора. Большинство расширений будут использовать версии автоматически.
к сожалению, единственное место, не дать понять, что вам не нужно делать это самостоятельно-это поддержка Циклическая документация по сбору мусора.
деталь:
Noddy's выделяются с помощью функции Noddy_new
положить в tp_new
слоты TypeObject
. Согласно документация, главное, что должна сделать" новая " функция, это вызвать tp_alloc
слот. Вы обычно не пишете tp_alloc
себя, и он просто по умолчанию PyType_GenericAlloc()
.
смотрим PyType_GenericAlloc()
в Исходным кодом на Python показывает ряд разделов, где он изменяется на основе PyType_IS_GC(type)
. Сначала он зовет _PyObject_GC_Malloc
вместо PyObject_Malloc
, а во-вторых он называет _PyObject_GC_TRACK(obj)
. [Обратите внимание, что все это PyObject_New
действительно это вызов PyObject_Malloc
а то tp_init
.]
аналогично, при освобождении вы называете tp_free
слот, который автоматически устанавливается в PyObject_GC_Del
классы Py_TPFLAGS_HAVE_GC
. PyObject_GC_Del
включает код, который делает то же самое, что и PyObject_GC_UnTrack
Итак, вызов untrack не нужен.
Я недостаточно опытен в API C сам, чтобы дать вам какие-либо советы, но есть много примеры в самих реализациях контейнера Python.
лично я бы сначала начал с реализации кортежа, так как он неизменен: объекты/tupleobject.c. Затем перейдите к dict
, list
и set
реализации для дальнейших заметок о mutable контейнеры:
Я не могу не заметить, что есть звонки на PyObject_GC_New()
, PyObject_GC_NewVar()
и PyObject_GC_Track()
во всем, а также имея Py_TPFLAGS_HAVE_GC
set.