Документация Python неофициальный перевод
Содержание страницы

Жизненный цикл объектовObject Life Cycle

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

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

На приведённой ниже диаграмме показан порядок событий, которые могут произойти в течение жизни объекта. Стрелка от 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 достижимый достижимый tp_init->reachable reachable->tp_init tp_traverse tp_traverse достижимый->tp_traverse не в циклической изоляции достижимый->tp_traverse периодический циклическая изоляция обнаружение финализирован? отмечен как финализирован? достижимый->финализирован? нет ссылок tp_finalize tp_finalize достижимый->tp_finalize воскрешён (возможно удалить отметка финализации) несобираемый несобираемый (утечка) достижимый->несобираемый циклический изоляция (без GC поддержка) tp_dealloc tp_dealloc достижимый->tp_dealloc нет ссылок tp_traverse->финализирован? циклический изолят финализирован?->tp_finalize нет (отметить как финализированный) tp_clear tp_clear финализирован?->tp_clear да tp_finalize->tp_clear нет ссылок или циклический изолированный tp_finalize->tp_dealloc рекомендуется вызов (см. объяснение) tp_finalize->tp_dealloc нет ссылок tp_clear->неподлежащий сборке циклический изолят 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 также может быть полностью пропущен, например, при вызове __new__() из кода Python.

  • После завершения 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. Это повторяется, пока циклический изолят всё ещё существует и не все объекты были очищены.

Уничтожение циклического изолятаCyclic Isolate Destruction

Ниже перечислены стадии жизни гипотетического циклического изолята, который продолжает существовать после финализации или очистки каждого объекта-участника. Это утечка памяти, если циклический изолят проходит через все эти стадии; он должен исчезнуть, как только все объекты будут очищены, если не раньше. Циклический изолят может исчезнуть либо из-за разрыва цикла ссылок, либо из-за того, что объекты больше не являются изолированными из-за воскрешения финализатором (см. 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 только по мере необходимости для уничтожения циклического изолята; это может измениться в будущей версии.

ФункцииFunctions

Для выделения и освобождения памяти см. Выделение объектов в куче.

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 для примера кода.