Содержание страницы
Жизненный цикл объектов¶Object Life Cycle
В этом разделе объясняется, как слоты типа соотносятся друг с другом на протяжении жизни объекта. Он не является полным каноническим справочником по слотам; вместо этого обращайтесь к документации по конкретному слоту в Структуры объектов типа за подробностями.
События жизненного цикла¶Life Events
На приведённой ниже диаграмме показан порядок событий, которые могут произойти в течение жизни объекта. Стрелка от A к B означает, что событие B может наступить после того, как произошло событие A, а подпись стрелки указывает условие, которое должно выполняться, чтобы B наступило после A.
Пояснение:
Когда новый объект создаётся вызовом его типа:
tp_newвызывается для создания нового объекта.tp_allocвызывается напрямуюtp_newдля выделения памяти под новый объект.tp_initинициализирует только что созданный объект.tp_initможет быть вызван снова для повторной инициализации объекта, если необходимо. Вызовtp_initтакже может быть полностью пропущен, например, при вызове__new__()из кода Python.
После завершения
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. Это повторяется, пока циклический изолят всё ещё существует и не все объекты были очищены.
Уничтожение циклического изолята¶Cyclic Isolate Destruction
Ниже перечислены стадии жизни гипотетического циклического изолята,
который продолжает существовать после финализации или очистки каждого объекта-участника. Это
утечка памяти, если циклический изолят проходит через все эти стадии; он должен
исчезнуть, как только все объекты будут очищены, если не раньше. Циклический изолят может
исчезнуть либо из-за разрыва цикла ссылок, либо из-за того, что объекты
больше не являются изолированными из-за воскрешения финализатором (см.
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 только
по мере необходимости для уничтожения циклического изолята; это может измениться в будущей версии.
Функции¶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для примера кода.