Spec-Zone.ru › Python 3.14

Жизненный цикл объекта

В этом разделе объясняется, как слоты типа соотносятся друг с другом на протяжении жизненного цикла объекта. Здесь не приводится полное каноническое описание слотов; сведения об отдельном слоте см. в документации по слотам в разделе Структуры объектов типов.

События жизненного цикла

На рисунке ниже показан порядок событий, которые могут происходить на протяжении жизненного цикла объекта. Стрелка от A к B указывает, что событие B может произойти после события A; подпись стрелки обозначает условие, которое должно выполняться, чтобы B могло произойти после A.

События жизненного цикла tp_new tp_new start->tp_new вызов типа tp_alloc tp_alloc tp_new->tp_alloc прямой вызов tp_init tp_init tp_new->tp_init reachable достижим tp_init->reachable reachable->tp_init tp_traverse tp_traverse reachable->tp_traverse не входит в циклический изолят reachable->tp_traverse периодическое обнаружение циклических изолятов finalized? помечен как финализированный? reachable->finalized? нет ссылок tp_finalize tp_finalize reachable->tp_finalize воскрешён (возможно, снята пометка финализации) uncollectable недоступен для сборки мусора (утечка) reachable->uncollectable циклический изолят (без поддержки сборки мусора) tp_dealloc tp_dealloc reachable->tp_dealloc нет ссылок tp_traverse->finalized? циклический изолят finalized?->tp_finalize нет (пометить как финализированный) tp_clear tp_clear finalized?->tp_clear да tp_finalize->tp_clear нет ссылок или циклический изолят tp_finalize->tp_dealloc рекомендуемый вызов (см. пояснение) tp_finalize->tp_dealloc нет ссылок tp_clear->uncollectable циклический изолят tp_clear->tp_dealloc нет ссылок tp_free tp_free tp_dealloc->tp_free прямой вызов

Пояснение:

  • Когда новый объект создаётся вызовом его типа:

    1. tp_new вызывается для создания нового объекта.
    2. tp_alloc напрямую вызывается функцией tp_new, чтобы выделить память для нового объекта.
    3. tp_init инициализирует только что созданный объект. При необходимости tp_init можно вызвать повторно, чтобы повторно инициализировать объект. Вызов tp_init также можно полностью пропустить, например, если код Python вызывает __new__().
  • После завершения tp_init объект готов к использованию.
  • Через некоторое время после удаления последней ссылки на объект:

    1. Если объект не помечен как финализированный, он может быть финализирован: для этого его помечают как финализированный и вызывают функцию tp_finalize. Python не финализирует объект при удалении последней ссылки на него; используйте PyObject_CallFinalizerFromDealloc(), чтобы гарантировать, что tp_finalize всегда вызывается.
    2. Если объект помечен как финализированный, сборщик мусора может вызвать tp_clear, чтобы очистить ссылки, удерживаемые объектом. Эта функция не вызывается при достижении счётчиком ссылок объекта нуля.
    3. tp_dealloc вызывается для уничтожения объекта. Чтобы избежать дублирования кода, tp_dealloc обычно вызывает tp_clear, чтобы освободить ссылки объекта.
    4. После завершения уничтожения объекта функцией 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).

  1. Достижимые (ещё не циклический изолят): Все объекты находятся в обычном, достижимом состоянии. Цикл ссылок может существовать, но внешняя ссылка означает, что объекты ещё не изолированы.
  2. Недостижимые, но непротиворечивые: Удалена последняя ссылка извне группы циклически связанных объектов, в результате чего объекты оказались изолированными (и возник циклический изолят). Ни один объект группы ещё не был финализирован или очищен. Циклический изолят остаётся на этом этапе до следующего запуска сборщика мусора (не обязательно ближайшего, поскольку при ближайшем запуске могут быть просканированы не все объекты).
  3. Часть объектов финализирована, часть — нет: Объекты в циклическом изоляте финализируются по одному, поэтому некоторое время циклический изолят состоит из финализированных и нефинализированных объектов. Порядок финализации не определён, поэтому он может казаться случайным. Финализированный объект должен вести себя разумно при взаимодействии с нефинализированными объектами, а нефинализированный объект должен переносить финализацию произвольного подмножества объектов, на которые он ссылается.
  4. Все объекты финализированы: Все объекты циклического изолята финализируются до того, как любой из них будет очищен.
  5. Часть объектов финализирована, часть — очищена: Объекты могут очищаться последовательно или одновременно (при удерживаемом GIL); в любом случае одни завершат очистку раньше других. Финализированный объект должен переносить очистку части объектов, на которые он ссылается. PEP 442 называет этот этап «циклическим мусором».
  6. Утечка: Если циклический изолят всё ещё существует после финализации и очистки всех объектов группы, эти объекты остаются недоступными для сборки мусора неопределённо долго (см. 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API