stringtranslate.com

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

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

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

Процесс

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

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

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

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

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

Варианты

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

Механизм

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

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

класс  Clingy :  def  __init__ ( self ,  ref = None )  ->  None :  self . ссылка  =  ссылка  def  __del__ ( self ):  если  self . ссылка :  сам . исх . ref  =  self  print ( «Не оставляй меня!» )a  =  Clingy ( Clingy ())  # Создайте связанный список из двух элементов,  # на который ссылается |a| а . исх . ref  =  a  # Создать цикл a . ref  =  None  # Очистка ссылки из первого узла  # на второй делает второй мусор a . ссылка  =  Нет

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

c  =  Нет class  Immortal :  def  __del__ ( self ):  global  c  c  =  self  print ( «Я еще не умер». )c  =  Бессмертный () c  =  Нет  # Очистка |c| превращает объект в мусор c  =  None

В приведенных выше примерах в 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, завершение объекта определяется «очередью» завершения [a] , которая проверяется во время уничтожения объекта. Объекты с финализатором помещаются в эту очередь при создании и удаляются из очереди при вызове финализатора, но могут быть исключены из очереди вручную (до финализации) с помощью SuppressFinalizeили повторно поставлены в очередь с помощью ReRegisterForFinalize. Таким образом, по умолчанию объекты с финализаторами финализируются не более одного раза, но эту финализацию можно подавить или объекты могут финализироваться несколько раз, если они воскрешаются (снова становятся доступными), а затем повторно ставятся в очередь для финализации. Кроме того, слабые ссылки по умолчанию не отслеживают воскрешение, то есть слабая ссылка не обновляется, если объект воскрешается; они называются короткими слабыми ссылками , а слабые ссылки, отслеживающие воскрешение, называются длинными слабыми ссылками . [8]

Приложения

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

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

Примечания

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

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

  1. ^ abc Гольдштейн, Зурбалев и Флатов 2012, с. 129.
  2. ^ аб Рихтер 2000.
  3. ^ ab «Что такое воскрешение (при сборке мусора)?». XYZWS. Архивировано из оригинала 23 ноября 2011 г. Проверено 1 августа 2011 г. Объект, допущенный к сбору мусора, может перестать соответствовать критериям и вернуться к нормальной жизни. В методе 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) . Hesab.net . Проверено 1 августа 2011 г. Воскрешение объектов — это продвинутый метод, который, вероятно, будет полезен только в необычных сценариях, например, когда вы реализуете пул объектов, создание и уничтожение которых занимает много времени. ... Демонстрационное приложение ObjectPool показывает, что менеджер пула объектов может повысить производительность, когда часто создается и уничтожается множество объектов. Предположим, у вас есть класс RandomArray, который инкапсулирует массив случайных чисел. Основная программа создает и уничтожает тысячи объектов RandomArray, хотя в данный момент активны лишь несколько объектов. Поскольку класс создает случайный массив в своем методе-конструкторе (операция, требующая много времени), эта ситуация идеальна для метода объединения. ... Важным моментом в методе объединения в пул является то, что класс PoolManager содержит ссылку на неиспользуемые объекты в пуле (в объекте PooledObjects Stack), но не на объекты, используемые основной программой. Фактически, последние объекты поддерживаются только за счет ссылок в основной программе. Когда основная программа присваивает объекту RandomArray значение Nothing (или позволяет ему выйти за пределы области видимости) и происходит сборка мусора, сборщик мусора вызывает метод Finalize объекта. Таким образом, код внутри метода Finalize RandomArray имеет возможность воскресить себя, сохранив ссылку на себя в структуре PooledObjects PoolManager. Поэтому, когда функция NewRandomArray вызывается снова, объект PoolManager может вернуть клиенту объект из пула, не проходя трудоемкий процесс создания нового.