Жизненный цикл объекта
В этом разделе объясняется, как слоты типа соотносятся друг с другом на протяжении жизненного цикла объекта. Здесь не приводится полное каноническое описание слотов; сведения об отдельном слоте см. в документации по слотам в разделе Структуры объектов типов.
События жизненного цикла
На рисунке ниже показан порядок событий, которые могут происходить на протяжении жизненного цикла объекта. Стрелка от A к B указывает, что событие B может произойти после события A; подпись стрелки обозначает условие, которое должно выполняться, чтобы B могло произойти после A.
Пояснение:
-
Когда новый объект создаётся вызовом его типа:
-
tp_newвызывается для создания нового объекта. -
tp_allocнапрямую вызывается функциейtp_new, чтобы выделить память для нового объекта. -
tp_initинициализирует только что созданный объект. При необходимостиtp_initможно вызвать повторно, чтобы повторно инициализировать объект. Вызовtp_initтакже можно полностью пропустить, например, если код Python вызывает__new__().
-
- После завершения
tp_initобъект готов к использованию. -
Через некоторое время после удаления последней ссылки на объект:
- Если объект не помечен как финализированный, он может быть финализирован: для этого его помечают как финализированный и вызывают функцию
tp_finalize. Python не финализирует объект при удалении последней ссылки на него; используйтеPyObject_CallFinalizerFromDealloc(), чтобы гарантировать, чтоtp_finalizeвсегда вызывается. - Если объект помечен как финализированный, сборщик мусора может вызвать
tp_clear, чтобы очистить ссылки, удерживаемые объектом. Эта функция не вызывается при достижении счётчиком ссылок объекта нуля. -
tp_deallocвызывается для уничтожения объекта. Чтобы избежать дублирования кода,tp_deallocобычно вызываетtp_clear, чтобы освободить ссылки объекта. - После завершения уничтожения объекта функцией
tp_deallocона напрямую вызываетtp_free(обычно автоматически устанавливаемую вPyObject_Free()илиPyObject_GC_Del(), в зависимости от типа), чтобы освободить память.
- Если объект не помечен как финализированный, он может быть финализирован: для этого его помечают как финализированный и вызывают функцию
- Функции
tp_finalizeразрешено при необходимости добавить ссылку на объект. В этом случае объект воскрешается, и его ожидающее уничтожение предотвращается. (Только функцииtp_finalizeразрешено воскрешать объект; функцииtp_clearиtp_deallocне могут этого сделать, не вызвавtp_finalize.) Воскрешение объекта может привести или не привести к снятию с него пометки финализированный. В настоящее время Python не снимает эту пометку с воскрешённого объекта, если тот поддерживает сборку мусора (то есть установлен флагPy_TPFLAGS_HAVE_GC), но снимает её, если объект не поддерживает сборку мусора; в будущем любое из этих поведений или оба могут измениться. -
tp_deallocможет при необходимости вызватьtp_finalizeчерезPyObject_CallFinalizerFromDealloc(), если требуется повторно использовать этот код для уничтожения объекта. Это рекомендуется, поскольку гарантирует, чтоtp_finalizeвсегда вызывается до уничтожения. Пример кода см. в документацииtp_dealloc. - Если объект входит в состав циклического изолята и функция
tp_clearне может разорвать цикл ссылок либо циклический изолят не обнаруживается (например, был вызванgc.disable()или для одного из участвующих типов ошибочно не установлен флагPy_TPFLAGS_HAVE_GC), объекты навсегда остаются недоступными для сборки мусора («утекают»). См.gc.garbage.
Если объект помечен как поддерживающий сборку мусора (флаг Py_TPFLAGS_HAVE_GC установлен в tp_flags), возможны также следующие события:
- Сборщик мусора время от времени вызывает
tp_traverse, чтобы обнаружить циклические изоляты. - Обнаружив циклический изолят, сборщик мусора финализирует один из объектов группы: помечает его как финализированный и вызывает его функцию
tp_finalize, если она есть. Это повторяется, пока циклический изолят не исчезнет или пока не будут финализированы все объекты. -
tp_finalizeможет воскресить объект, добавив ссылку из-за пределов циклического изолята. Новая ссылка приводит к тому, что группа объектов перестаёт образовывать циклический изолят (цикл ссылок может сохраниться, но в этом случае объекты больше не будут изолированными). - Если сборщик мусора обнаруживает циклический изолят, в котором все объекты группы уже помечены как финализированные, он очищает один или несколько ещё не очищенных объектов группы (возможно, одновременно), вызывая функцию
tp_clearкаждого из них. Это повторяется, пока циклический изолят существует и не все объекты очищены.
Уничтожение циклического изолята
Ниже перечислены этапы жизненного цикла гипотетического циклического изолята, который продолжает существовать после финализации или очистки каждого входящего в него объекта. Если циклический изолят проходит все эти этапы, это утечка памяти; он должен исчезнуть, когда все объекты будут очищены, или раньше. Циклический изолят может исчезнуть либо из-за разрыва цикла ссылок, либо потому, что объекты перестают быть изолированными вследствие воскрешения финализатором (см. tp_finalize).
- Достижимые (ещё не циклический изолят): Все объекты находятся в обычном, достижимом состоянии. Цикл ссылок может существовать, но внешняя ссылка означает, что объекты ещё не изолированы.
- Недостижимые, но непротиворечивые: Удалена последняя ссылка извне группы циклически связанных объектов, в результате чего объекты оказались изолированными (и возник циклический изолят). Ни один объект группы ещё не был финализирован или очищен. Циклический изолят остаётся на этом этапе до следующего запуска сборщика мусора (не обязательно ближайшего, поскольку при ближайшем запуске могут быть просканированы не все объекты).
- Часть объектов финализирована, часть — нет: Объекты в циклическом изоляте финализируются по одному, поэтому некоторое время циклический изолят состоит из финализированных и нефинализированных объектов. Порядок финализации не определён, поэтому он может казаться случайным. Финализированный объект должен вести себя разумно при взаимодействии с нефинализированными объектами, а нефинализированный объект должен переносить финализацию произвольного подмножества объектов, на которые он ссылается.
- Все объекты финализированы: Все объекты циклического изолята финализируются до того, как любой из них будет очищен.
- Часть объектов финализирована, часть — очищена: Объекты могут очищаться последовательно или одновременно (при удерживаемом GIL); в любом случае одни завершат очистку раньше других. Финализированный объект должен переносить очистку части объектов, на которые он ссылается. PEP 442 называет этот этап «циклическим мусором».
-
Утечка: Если циклический изолят всё ещё существует после финализации и очистки всех объектов группы, эти объекты остаются недоступными для сборки мусора неопределённо долго (см.
gc.garbage). Достижение этого этапа циклическим изолятом является ошибкой: это означает, что методыtp_clearучаствующих объектов не разорвали цикл ссылок, как того требует спецификация.
Если бы не существовало tp_clear, у Python не было бы способа безопасно разорвать цикл ссылок. Простое уничтожение объекта в циклическом изоляте привело бы к появлению висячего указателя и вызвало бы неопределённое поведение при уничтожении объекта, ссылающегося на уничтоженный объект. Этап очистки превращает уничтожение объектов в двухфазный процесс: сначала вызывается tp_clear, чтобы частично уничтожить объекты и разъединить их, затем вызывается tp_dealloc для завершения уничтожения.
В отличие от очистки, финализация не является этапом уничтожения. Финализированный объект должен продолжать работать должным образом, соблюдая предусмотренные для него контракты. Финализатор объекта может выполнять произвольный код Python и даже предотвращать предстоящее уничтожение, добавляя ссылку. Финализация связана с уничтожением только порядком вызовов: если она выполняется, то до уничтожения, которое начинается с tp_clear (если он вызывается) и завершается вызовом tp_dealloc.
Этап финализации не требуется для безопасного освобождения объектов в циклическом изоляте, однако его наличие упрощает проектирование типов, которые ведут себя разумно при очистке объектов. Очистка объекта может неизбежно оставить его в повреждённом, частично уничтоженном состоянии: вызывать методы очищенного объекта или обращаться к его атрибутам может быть небезопасно. При наличии финализации взаимодействовать с очищенными объектами могут только финализированные объекты; гарантируется, что нефинализированные объекты взаимодействуют только с неочищенными (но потенциально финализированными) объектами.
Возможные взаимодействия можно обобщить следующим образом:
- Нефинализированный объект может иметь ссылки на нефинализированные и финализированные объекты или ссылки от них, но не на очищенные объекты и не от них.
- Финализированный объект может иметь ссылки на нефинализированные, финализированные и очищенные объекты или ссылки от них.
- Очищенный объект может иметь ссылки на финализированные и очищенные объекты или ссылки от них, но не на нефинализированные объекты и не от них.
При отсутствии циклов ссылок объект можно просто уничтожить после удаления последней ссылки на него; этапы финализации и очистки не нужны для безопасного освобождения неиспользуемых объектов. Однако может быть полезно в любом случае автоматически вызывать tp_finalize и tp_clear перед уничтожением, поскольку проектирование типов упрощается, если все объекты всегда проходят одну и ту же последовательность событий, независимо от того, входили ли они в циклический изолят. В настоящее время Python вызывает tp_finalize и tp_clear только при необходимости уничтожить циклический изолят; в будущей версии это может измениться.
Функции
Информацию о выделении и освобождении памяти см. в разделе Выделение объектов в куче.
-
void PyObject_CallFinalizer(PyObject *op) -
Финализирует объект, как описано в разделе
tp_finalize. Вызывайте эту функцию (илиPyObject_CallFinalizerFromDealloc()) вместо прямого вызоваtp_finalize, поскольку эта функция может объединять несколько вызововtp_finalize. В настоящее время вызовы объединяются только для типов, поддерживающих сборку мусора (то есть, если установлен флагPy_TPFLAGS_HAVE_GC); в будущем это может измениться.Добавлено в версии 3.4.
-
int PyObject_CallFinalizerFromDealloc(PyObject *op) -
То же, что и
PyObject_CallFinalizer(), но предназначена для вызова в начале деструктора объекта (tp_dealloc). На объект не должно быть ссылок. Если финализатор объекта воскрешает его, эта функция возвращает -1; дальнейшее уничтожение выполнять не следует. В противном случае функция возвращает 0, и уничтожение может продолжаться обычным образом.Добавлено в версии 3.4.
См. также
Пример кода см. в разделе
tp_dealloc.
© 2001 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/c-api/lifecycle.html