Поддержка циклической сборки мусора
Поддержка Python обнаружения и сборки мусора, включая циклические ссылки, требует поддержки являющихся «контейнерами» для других объектов типов объектов, которые также могут быть контейнерами. Не хранящие ссылки на другие объекты типы или хранящие только ссылки на атомарные типы (например, числа или строки), не нуждаются в явной реализации сборки мусора.
Чтобы создать тип контейнера, поле tp_flags
объекта
типа должно включать Py_TPFLAGS_HAVE_GC
и обеспечивать реализацию
обработчика tp_traverse
. Если экземпляры типа
изменяемы, также должна быть предоставлена реализация
tp_clear
.
-
Py_TPFLAGS_HAVE_GC
Объекты с типом, для которого установлен данный флаг, должны соответствовать рассмотренным здесь правилам. Для удобства объекты будем называть объектами-контейнерами.
Конструкторы для типов контейнеров должны соответствовать двум правилам:
- Память для объекта должна быть выделена с использованием
PyObject_GC_New()
илиPyObject_GC_NewVar()
. - После инициализации всех полей, которые могут содержать ссылки на другие
контейнеры, необходимо вызвать
PyObject_GC_Track()
.
-
TYPE*
PyObject_GC_New
(TYPE, PyTypeObject *type) Аналогично
PyObject_New()
, но для объектов-контейнеров с установленным флагомPy_TPFLAGS_HAVE_GC
.
-
TYPE*
PyObject_GC_NewVar
(TYPE, PyTypeObject *type, Py_ssize_t size) Аналогично
PyObject_NewVar()
, но для объектов-контейнеров с установленным флагомPy_TPFLAGS_HAVE_GC
.
-
TYPE*
PyObject_GC_Resize
(TYPE, PyVarObject *op, Py_ssize_t newsize) Изменить размер объекта, выделенного
PyObject_NewVar()
. Возвращает объект с измененным размером илиNULL
в случае ошибки. op пока не должен отслеживаться сборщиком.
-
void
PyObject_GC_Track
(PyObject *op) Добавляет объект op в множество объектов-контейнеров, отслеживаемых сборщиком. Сборщик может запускаться в неожиданное время, поэтому объекты должны быть действительными во время отслеживания. Это следует вызывать, когда все поля, за которыми следует обработчик
tp_traverse
, становятся действительными, обычно ближе к концу конструктора.
Точно так же освободитель для объекта должен соответствовать аналогичной паре правил:
- Прежде чем поля, которые относятся к другим контейнерам, станут
недействительными, необходимо вызвать
PyObject_GC_UnTrack()
. - Память объекта должна быть освобождена с помощью
PyObject_GC_Del()
.
-
void
PyObject_GC_Del
(void *op) Освобождает память, выделенную объекту, используя
PyObject_GC_New()
илиPyObject_GC_NewVar()
.
-
void
PyObject_GC_UnTrack
(void *op) Удалить объект op из набора объектов-контейнеров, отслеживаемых сборщиком. Обратите внимание, что
PyObject_GC_Track()
можно снова вызвать для этого объекта, чтобы добавить его обратно в множество отслеживаемых объектов. Освободитель (обработчикtp_dealloc
) должен вызвать это для объекта до того, как какое-либо из полей, используемых обработчикомtp_traverse
, станет недействительным.
Изменено в версии 3.8: Макросы _PyObject_GC_TRACK()
и _PyObject_GC_UNTRACK()
были
удалены из общедоступного C API.
Обработчик tp_traverse
принимает параметр функции
этого типа:
-
int
(*visitproc)
(PyObject *object, void *arg) Тип функции посетителя, переданной обработчику
tp_traverse
. Функция должна быть вызвана с объектом для обхода как object и третьим параметром обработчикаtp_traverse
как arg. Ядро Python использует несколько функций посетителя для реализации циклического обнаружения мусора; не ожидается, что пользователям потребуется писать свои собственные функции для посетителей.
Обработчик tp_traverse
должен быть следующего типа:
-
int
(*traverseproc)
(PyObject *self, visitproc visit, void *arg) Функция обхода для объекта-контейнера. Реализации должны вызывать функцию visit для каждого объекта, непосредственно содержащегося в self, при этом параметры для visit являются содержащимся объектом, а значение arg передается обработчику. Функцию visit нельзя вызывать с аргументом объекта
NULL
. Если visit возвращает ненулевое значение, это значение должно быть возвращено немедленно.
Чтобы упростить написание обработчиков tp_traverse
,
предоставляется макрос Py_VISIT()
. Чтобы использовать данный макрос,
реализация tp_traverse
должна назвать свои аргументы
точно visit и arg:
-
void
Py_VISIT
(PyObject *o) Если o не
NULL
, вызвать обратный вызов visit с аргументами o и arg. Если visit возвращает ненулевое значение, возвращает его. Используя данный макрос, обработчикиtp_traverse
выглядят так:static int my_traverse(Noddy *self, visitproc visit, void *arg) { Py_VISIT(self->foo); Py_VISIT(self->bar); return 0; }
Обработчик tp_clear
должен быть типа
inquiry
или NULL
, если объект неизменяемый.
-
int
(*inquiry)
(PyObject *self) Отбросить ссылки, которые могли создать ссылочные циклы. Неизменяемые объекты не должны определять данный метод, поскольку они никогда не могут напрямую создавать ссылочные циклы. Обратите внимание, что объект должен оставаться действительным после вызова этого метода (не вызывайте просто
Py_DECREF()
по ссылке). Сборщик вызовет данный метод, если обнаружит, что данный объект участвует в цикле обращения.