Почему finalize() вызывается сборщиком мусора только один раз?
Цитаты из учебного пособия SCJP 6:
в
finalize()
Методом, которым вы могли бы написать код, который передает ссылку на рассматриваемый объект обратно к другому объекту, эффективно исключая объект из списка для сборки мусора. Если в какой-то момент позже этот же объект снова становится пригодным для сбора мусора, сборщик мусора все еще может обработать этот объект и удалить его. Однако сборщик мусора будет помнить, что для этого объектаfinalize()
уже побежал, и он не побежитfinalize()
снова
Почему так устроено? Цель finalize()
Метод по-прежнему остается в силе, даже когда объект отмечен как коллекция во второй раз. Тогда почему Java решает пропустить вызов finalize()
?
2 ответа
Я не знаю, является ли это первоначальной причиной, но текущая реализация ставит в очередь Finalizer
экземпляры (внутренний подкласс Reference
) для объектов, переопределяющих метод finalize внутренним ReferenceQueue
который опрашивается преданным FinalizerThread
,
И поскольку у JVM нет возможности узнать, нужно ли завершать объект во второй раз, он не может решить, нужно ли ставить новый Finalizer
однажды finalize()
метод был вызван.
Во всяком случае, вы должны избегать использования finalize()
, Это делает выделение объектов более дорогостоящим, предотвращает аварийный анализ и не очень надежный способ управления собственными ресурсами, поскольку сборщик мусора может отложить завершение на неограниченное количество времени.
Объекты с включенным финализатором не подходят для сбора; однако GC проверяет их только после определения всех других объектов, которые не могут быть собраны, и записывает все объекты, которые могли бы быть собраны, кроме наличия включенного финализатора, и запускает finalize
методы таких объектов, как только практические. Финализуемые объекты не будут допущены к сбору до тех пор, пока не будет запущен финализатор, но GC не будет иметь возможности различать объекты, которые могут быть допущены к финализации после его завершения, или те, которые были признаны непригодными для финализации в результате действий. финализатором какого-либо объекта и получил право на коллекцию в более позднее время.
.NET Framework включает методы, называемые IIRC GC.SuppressFinalize
а также GC.ReRegisterForFinalization
что делает возможным для кода, который знает, что финализатор объекта не сделает ничего полезного, чтобы сказать GC, чтобы не беспокоить его вызов, и позволяет коду, который знает, что финализатор запустился "слишком рано", чтобы запросить его запуск позже. Однако в JVM такой функции нет. Поскольку автоматическое перерегистрация всех финализуемых объектов для финализации после запуска финализатора не позволит собрать их вообще, и поскольку нет никакой возможности перерегистрировать их вручную, общее следствие состоит в том, что не существует пригодного шаблона, по которому финализатор объекта может быть запущен более, чем один раз.
С другой стороны, можно добиться аналогичного эффекта, определив объект вложенного класса, который является финализируемым, с помощью объекта внешнего класса, содержащего ссылку на экземпляр вложенного класса, и с помощью цепочки методов "finalize" этого вложенного класса обратно очистить код в его владельце. Если этот код очистки отбрасывает экземпляр вложенного класса и заменяет его новым, то этот новый экземпляр будет запускать свой финализатор (цепочку обратно к своему владельцу) в следующем цикле GC, где обнаруживается, что владелец не имеет ссылки.