Если сборщики мусора Java обходят граф живых объектов, как они узнают, для каких объектов вызывать finalize()?

Насколько я понимаю, GC, такие как ParallelGC и G1, являются сборщиками «поколения». Сборка мусора происходит почти как побочный продукт, поскольку вы перемещаете все живые объекты в новую область кучи, и все, что осталось в старой области, будет просто перезаписано. Это «побочное» объяснение имеет большой смысл, за исключением той части, где Java должен вызывать finalize() для мертвых объектов. Сохраняет ли Java отдельный список всех объектов в каждой области кучи, который можно сравнивать с живыми объектами?


person user7382368    schedule 30.01.2020    source источник


Ответы (2)


Да, GC отслеживает все эти Объекты и их типы.

На самом деле существует специальная фаза для GC, которая имеет дело только с этими специальными ссылками: WeakReference, SoftReference, PhantomReference и искусственными Finalizer. Некоторые называют это Cleanup phase, некоторые Reference Processing; в их составе есть фазы Pre-cleapup и Post-cleanup.

Но идея в том, что когда GC встречает такую ​​специальную ссылку во время фазы маркировки, она следит за ней. Сначала он отслеживает их отдельно (вспомните: регистрирует их в специальном List). По завершении фазы mark (по крайней мере, в течение GCс) эти ссылки будут анализироваться с паузой (stop-the-world). С некоторыми из них не так сложно работать: WeakReferences и SoftReferences — самые простые: если referent слабо/мягко достижимо, вернуть его и отправить специальное событие ReferenceQueue. PhantomReferences почти одинаковы (есть разница между java-8 и 9, но не буду вдаваться в подробности).

... где Java должен вызвать finalize() для мертвых объектов

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

person Eugene    schedule 11.03.2020
comment
«…только убить его сразу после» было бы здорово, если бы GC мог это сделать, но на самом деле это далеко не «сразу», когда у объектов есть нетривиальный метод finalize() - person Holger; 11.03.2020
comment
@Holger, я имел в виду, что это было сразу после следующего цикла, я думаю, следует отредактировать - person Eugene; 11.03.2020
comment
…в лучшем случае… - person Holger; 11.03.2020
comment
@Holger попал в точку, да. Это минное поле, слова надо подбирать тщательно, спасибо за отзыв. - person Eugene; 11.03.2020

Сохраняет ли Java отдельный список всех объектов в каждой области кучи, который можно сравнивать с живыми объектами?

Подумайте об этом на мгновение -> Список всех объектов в куче, где вы могли бы найти что-то подобное? Ответ довольно прост и понятен, место, где вы можете найти все объекты в куче, — это куча.

Сборка мусора происходит почти как побочный продукт, поскольку вы перемещаете все живые объекты в новую область кучи, и все, что осталось в старой области, будет просто перезаписано. Это «побочное» объяснение имеет большой смысл, за исключением той части, где Java должен вызывать finalize() для мертвых объектов.

Почему это может быть проблемой? Как вы правильно заметили, все живые объекты обрабатываются (либо перемещаются в следующее пространство кучи, либо стареют). Во время сборки мусора (как второстепенной, так и основной) вы проверяете ссылки на все объекты в обрабатываемом пространстве кучи (вы не знаете, какие из них живые/мертвые до проверки), это означает, что вы точно знаете, какие из них живые, а какие впоследствии мертвы -> что мешает вам вызвать finalize() для мертвых объектов? Вы получаете к ним доступ прямо из кучи, чтобы вы могли это сделать.

Кроме того, в качестве ресурса, более подробно объясняющего сборку мусора, я все еще нахожу Основы сборки мусора в Java довольно хороши, особенно учитывая пошаговый пример сборки мусора с помощью поколений.

person Worthless    schedule 30.01.2020
comment
Список следующий: прямо здесь, поле static Finalizer unfinalized; указывает на первый узел, поля private Finalizer next, prev; составляют связанный список. Но он содержит только объекты, классы которых на самом деле имеют объявленный метод finalize() (сам Finalizer не имеет такого метода, и пустой метод java.lang.Object не считается, иначе это не сработает). - person Holger; 31.01.2020