Воскрешение объекта - Object resurrection

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

Воскрешение объекта вызывает ряд проблемы, в частности, возможность воскрешения объекта - даже если оно не происходит - значительно усложняет и замедляет сборку мусора и является основной причиной, по которой финализаторы не приветствуются. Языки имеют дело с воскрешением объектов по-разному, например решения к этим проблемам. В редких случаях воскрешение объекта используется для реализации определенных шаблонов проектирования, в частности пул объектов,[1] в то время как в других обстоятельствах воскрешение является нежелательной ошибкой, вызванной ошибкой в ​​финализаторах, и в целом воскрешение не рекомендуется.[2]

Процесс

Воскрешение объекта происходит следующим образом. Сначала объект становится мусор когда он больше недоступен для программы, и может быть собран (уничтожен и освобожден). Затем во время уничтожения объекта, прежде чем сборщик мусора освободит объект, финализатор может быть запущен, что, в свою очередь, может сделать этот объект или другой объект мусора (доступный из объекта с помощью финализатора) снова доступным путем создания ссылок на него, поскольку финализатор может содержать произвольный код. Если это произойдет, объект, на который указывает ссылка - который не обязательно является финализированным объектом - больше не является мусором и не может быть освобожден, поскольку в противном случае ссылки на него стали бы висячие ссылки и вызывают ошибки при использовании, как правило, сбой программы или непредсказуемое поведение. Вместо этого, чтобы поддерживать безопасность памяти, объект возвращается к жизни или воскрес.

Чтобы обнаружить это, сборщик мусора обычно выполняет двухфазный сбор при наличии финализаторов: сначала финализируйте любой мусор, у которого есть финализатор, а затем перепроверьте все мусор (или весь мусор, доступный для объектов с финализаторами), если финализаторы воскресили какой-то мусор. Это увеличивает накладные расходы и задерживает восстановление памяти.

Воскрешенные объекты

С воскресшим объектом можно обращаться так же, как и с другими объектами, или с ним можно обращаться особым образом. Во многих языках, особенно C #, Java и Python (начиная с Python 3.4), объекты финализируются только один раз, чтобы избежать возможности многократного воскрешения объекта или даже его нерушимости; в C # объекты с финализаторами по умолчанию финализируются только один раз, но могут быть повторно зарегистрированы для финализации. В других случаях воскрешенные объекты считаются ошибками, особенно в Objective-C; или обрабатываются идентично другим объектам, особенно в Python до Python 3.4.

Воскресший объект иногда называют зомби-объект или же зомби, но этот термин используется для различных состояний объекта, связанных с уничтожением объекта, в зависимости от языка и автора. «Зомби-объект» имеет особое значение в Цель-C, однако, что подробно описано ниже. Объекты зомби чем-то похожи на зомби процессы, в том, что они претерпели изменение состояния завершения и близки к освобождению, но детали значительно отличаются.

Варианты

в .NET Framework, особенно C # и VB.NET, «воскрешение объекта» вместо этого относится к состоянию объекта. в течение финализация: объект возвращается к жизни (из недоступности), финализатор запускается, а затем возвращается в состояние недоступности (и больше не регистрируется для будущей финализации). В .NET объекты, требующие завершения, не отслеживаются по отдельности, а хранятся в «очереди» завершения,[а] поэтому вместо понятия воскрешенных объектов в смысле этой статьи мы говорим об объектах, «поставленных в очередь на завершение». Кроме того, объекты могут быть повторно поставлены в очередь для завершения через GC.ReRegisterForFinalize, стараясь не умножать объекты в очереди.[2]

Механизм

Существует два основных способа, которыми объект может воскресить себя или другой объект: путем создания ссылки на себя в объекте, доступном для него (мусор недоступен, но мусор может ссылаться на объекты, не являющиеся мусором) или путем создания ссылки в окружающая среда (глобальные переменные, или в некоторых случаях статические переменные или переменные в закрытие ). Ниже приведены примеры того и другого в Python для объекта, который сам себя воскрешает. Также возможно, что объект воскресит другие объекты, если они оба собираются в данном цикле сборки мусора с помощью одних и тех же механизмов.

Воскрешает, создавая ссылку на объект, которого он может достичь:

учебный класс Цепкий:    def __в этом__(себя, ссылка=Никто) -> Никто:        себя.ссылка = ссылка            def __del__(себя):        если себя.ссылка:            себя.ссылка.ссылка = себя        Распечатать("Не оставляй меня!")а = Цепкий(Цепкий())  # Создайте связанный список из 2 элементов,                      # на который ссылается | a |а.ссылка.ссылка = а  # Создаем цикла.ссылка = Никто  # Удаление ссылки с первого узла              # второму делает вторую фигнюа.ссылка = Никто

Воскрешает, создавая ссылку в глобальной среде:

c = Никтоучебный класс Бессмертный:    def __del__(себя):        Глобальный c        c = себя        Распечатать(«Я еще не умер».)c = Бессмертный()c = Никто  # Очистка | c | делает объект мусоромc = Никто

В приведенных выше примерах в CPython до 3.4 они будут запускать финализаторы повторно, и объекты не будут собираться мусором, в то время как в CPython 3.4 и более поздних версиях финализаторы будут вызываться только один раз, а объекты будут собираться мусором. во второй раз они становятся недоступными.

Проблемы

Воскрешение объекта вызывает большое количество проблем.

Усложняет сборку мусора
Возможность воскрешения объекта означает, что сборщик мусора должен проверять воскрешенные объекты после завершения - даже если на самом деле этого не происходит, - что усложняет и замедляет сборку мусора.
Неразрушаемые объекты
В некоторых случаях объект может быть неразрушимым: если объект воскресает в своем собственном финализаторе (или группа объектов воскрешает друг друга в результате своих финализаторов), и финализатор всегда вызывается при уничтожении объекта, то объект не может быть уничтоженным, и его память не может быть восстановлена.
Случайное воскрешение и утечки
В-третьих, воскрешение объекта может быть непреднамеренным, и результирующий объект может быть семантическим мусором, поэтому фактически никогда не собирался, вызывая логическое утечка памяти.
Несогласованное состояние и повторная инициализация
Воскрешенный объект может находиться в несовместимом состоянии или нарушать инварианты классов из-за того, что финализатор был выполнен и вызвал неправильное состояние. Таким образом, воскрешенные объекты, как правило, необходимо повторно инициализировать вручную.[1]
Уникальная доработка или повторная доработка
В некоторых языках (таких как Java и Python 3.4+) финализация гарантированно происходит ровно один раз для каждого объекта, поэтому у воскрешенных объектов не будут вызываться финализаторы; поэтому воскрешенные объекты должны выполнять любой необходимый код очистки вне финализатора. В некоторых других языках программист может принудительно выполнять финализацию повторно; в частности, C # имеет GC.ReRegisterForFinalize.[1]

Решения

В языках принято несколько различных методов для решения проблемы воскрешения объекта, чаще всего с помощью двухфазной сборки мусора в присутствии финализаторов, чтобы предотвратить висячие ссылки; и только завершая объекты один раз, в частности, отмечая объекты как завершенные (с помощью флага), чтобы гарантировать, что объекты могут быть уничтожены.

Java не освободит объект, пока не докажет, что объект снова недоступен, но не будет запускать финализатор более одного раза.[3]

В Python до Python 3.4 стандарт CPython Реализация будет обрабатывать воскрешенные объекты так же, как другие объекты (которые никогда не были завершены), что делает возможными неразрушаемые объекты.[4] Кроме того, не будет циклов сборки мусора, содержащих объект с финализатором, чтобы избежать возможных проблем с воскрешением объекта. Начиная с Python 3.4, поведение во многом такое же, как и в Java:[b] объекты финализируются только один раз (помечаются как «уже завершенные»), сборка мусора циклов состоит из двух фаз, при этом вторая фаза проверяет воскрешенные объекты.[5][6]

Objective-C 2.0 переведет воскресшие объекты в состояние «зомби», где они будут записывать все отправленные им сообщения, но больше ничего не делать.[7] Смотрите также Автоматический подсчет ссылок: обнуление слабых ссылок для обработки слабые ссылки.

В .NET Framework, особенно в C # и VB.NET, финализация объекта определяется «очередью» финализации,[а] который проверяется во время уничтожения объекта. Объекты с финализатором помещаются в эту очередь при создании и удаляются из очереди при вызове финализатора, но могут быть удалены вручную (до завершения) с помощью Подавить или повторно поставлен в очередь с ReRegisterForFinalize. Таким образом, по умолчанию объекты с финализаторами финализируются не более одного раза, но это финализация может быть подавлена, или объекты могут быть финализированы несколько раз, если они воскреснут (снова станут доступными), а затем снова поставлены в очередь для завершения. Дальше, слабые ссылки по умолчанию не отслеживает воскрешение, то есть слабая ссылка не обновляется, если объект воскресает; они называются короткие слабые ссылки, а слабые ссылки, отслеживающие воскрешение, называются длинные слабые ссылки.[8]

Приложения

Воскрешение объекта полезно для обработки пул объектов часто используемых объектов, но это затемняет код и делает его более запутанным.[3] Его следует использовать только для часто используемых объектов, строительство / разрушение которых требует много времени. Примером может служить массив случайных чисел, в котором большое количество из них создается и уничтожается за короткое время, но на самом деле одновременно используется только небольшое их количество. При воскрешении объекта метод объединения уменьшит ненужные накладные расходы на создание и разрушение. Здесь менеджер пула получит информацию о своем стеке объектов в виде ссылки на объект, если он в настоящее время подлежит уничтожению. Менеджер пула сохранит объект для последующего использования.[9]

Смотрите также

Примечания

  1. ^ а б Это не совсем очередь, так как элементы могут быть удалены из середины с помощью GC.SuppressFinalization.
  2. ^ CPython использует счетчики ссылок для нециклического мусора с отдельным детектором цикла, в то время как большинство реализаций Java используют сборщик мусора трассировки.

Рекомендации

  1. ^ а б c Гольдштейн, Зурбалев и Флатов 2012, п.129.
  2. ^ а б Рихтер 2000.
  3. ^ а б «Что такое воскрешение (в сборке мусора)?». http://www.xyzws.com/: XYZWS. Получено 2011-08-01. Объект, который был допущен к сборке мусора, может перестать соответствовать требованиям и вернуться к нормальной жизни. В методе finalize () вы можете назначить это ссылочной переменной и предотвратить сбор этого объекта, что многие разработчики называют воскрешением. / Метод finalize () никогда не вызывается JVM более одного раза для любого заданного объекта. JVM не будет повторно вызывать метод finalize () после воскрешения (поскольку метод finalize () уже был запущен для этого объекта).
  4. ^ Ответ Тима Питерса к "Сколько раз можно вызвать `__del__` для каждого объекта в Python? "
  5. ^ Что нового в Python 3.4, PEP 442: Завершение безопасного объекта
  6. ^ Питру, Антуан (2013). «PEP 442 - Безопасная доработка объекта».
  7. ^ Реализация метода финализации
  8. ^ Гольдштейн, Зурбалев и Флатов 2012, п.131.
  9. ^ «Воскрешение объекта» (PDF). http://www.hesab.net/: Hesab.net. Получено 2011-08-01. Воскрешение объектов - это продвинутый метод, который может быть полезен только в необычных сценариях, например, когда вы реализуете пул объектов, создание и уничтожение которых требует много времени. ... Демонстрационное приложение ObjectPool показывает, что диспетчер пула объектов может повысить производительность, когда многие объекты часто создаются и уничтожаются. Предположим, что у вас есть класс RandomArray, который инкапсулирует массив случайных чисел. Основная программа создает и уничтожает тысячи объектов RandomArray, хотя в данный момент живы лишь несколько объектов. Поскольку класс создает случайный массив в своем методе конструктора (операция, требующая много времени), эта ситуация идеальна для метода объединения. ... Ключевым моментом в методе объединения является то, что класс PoolManager содержит ссылку на неиспользуемые объекты в пуле (в объекте стека PooledObjects), но не на объекты, используемые основной программой. Фактически, последние объекты поддерживаются только ссылками в основной программе. Когда основная программа устанавливает для объекта RandomArray значение Nothing (или позволяет ему выйти за пределы области видимости) и происходит сборка мусора, сборщик мусора вызывает метод Finalize объекта. Таким образом, код внутри метода Finalize RandomArray имеет возможность воскресить себя, сохранив ссылку на себя в структуре PooledObjects PoolManager. Поэтому, когда функция NewRandomArray вызывается снова, объект PoolManager может возвращать объект пула клиенту, не проходя трудоемкий процесс создания нового.