Какая ссылка Finalizer (FinalReference) или Weak/Phantom/Soft Reference имеет более высокий приоритет для GC

Когда создается объект с нетривиальным методом finalize(), JVM создаст Finalizer (FinalReference) с этим объектом в качестве референта. Что произойдет, если этот объект также будет обернут Soft/Weak или Phantom Reference? Попытается ли GC сначала поставить в очередь Finalizer (вызов метода finalize для него), а затем поставить в очередь другую ссылку или наоборот?


person Martin    schedule 03.10.2019    source источник
comment
Вы не должны использовать finalize(). Есть много вопросов и ответов по этому поводу – вот один – и освещение в книгах (например, «Essential Java» Джошуа Блоха).   -  person kaan    schedule 04.10.2019
comment
Вы правы, но этот вопрос для лучшего понимания поведения сборщика мусора, а не потому, что я хотел бы использовать finalize().   -  person Martin    schedule 04.10.2019


Ответы (1)


Я думаю, ваш вопрос не о времени постановки в очередь, на самом деле.

Обратите внимание на раздел Уведомление документа документация пакета

Через некоторое время после того, как сборщик мусора определит, что достижимость референта изменилась на значение, соответствующее типу ссылки, он добавит ссылку в соответствующую очередь.

(Обратите внимание на «некоторое время спустя»)

и аналогично, все ссылочные типы имеют выражение вида:

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

(взято из WeakReference; Обратите внимание на «в то же время или позже»)

На практике сборщик мусора передает обнаруженные ссылки другому потоку, который ставит их в очередь асинхронно. Поскольку неуказанная задержка делает порядок, в котором приложение будет извлекать ссылки из очереди, недетерминированным, бессмысленно спрашивать здесь о порядке.

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

Когда имеется смесь нескольких объектов ссылок с разными типами, включая ссылку Finalizer, но нет строгой ссылки, возможны два сценария:

  1. Есть по крайней мере одна мягкая ссылка и нет давления памяти. Затем сборщик мусора может решить, что нет очистки соответственно. поставить в очередь любую ссылку.
  2. Мягкая ссылка отсутствует или сборщик мусора решает, что нехватка памяти оправдывает очистку мягких ссылок. Затем все мягкие и слабые ссылки очищаются, и все мягкие, слабые ссылки и ссылки финализатора передаются для постановки в очередь.
    Только фантомные ссылки остаются нетронутыми.

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

  1. Есть по крайней мере одна мягкая ссылка и нет давления памяти. Затем сборщик мусора может решить, что нет очистки соответственно. поставить в очередь любую ссылку.
  2. Мягкая ссылка отсутствует или сборщик мусора решает, что нехватка памяти оправдывает очистку мягких ссылок. Затем все мягкие, слабые и фантомные ссылки очищаютсяA, и все мягкие, слабые и фантомные ссылки передаются для постановки в очередь.

A фантомные ссылки очищаются в Java 9 или более поздней версии. В предыдущих версиях они только ставились в очередь без очистки.

person Holger    schedule 23.10.2019
comment
Спасибо за такой отличный ответ. Одна вещь действительно интересна - Finalizer ссылочный объект последовательно блокирует Phantom ссылочный объект от постановки в очередь. Таким образом, объект со смесью ссылок Finalizer и Phantom потребует не менее 3 циклов сборки мусора (первый - поставить в очередь финализатора, второй - поставить в фантомную очередь, третий - удалить из кучи) для удаления из куча. Я прав? - person Martin; 23.10.2019
comment
Да, до Java 9 так и было. Начиная с Java 9 фантомные ссылки очищаются перед помещением в очередь, что позволяет восстановить память референта прямо в этот момент, поэтому это можно сделать за два цикла. - person Holger; 24.10.2019
comment
Вы уверены насчет этих двух циклов? Насколько я помню, обработка ссылок происходит в фазе Remark, поэтому давайте рассмотрим Старый объект со смесью ссылок Finalizer и Phantom. GC выполнит: 1-й цикл — поставит в очередь ссылку Final 2-й цикл — очистит и поставит в очередь ссылку Phantom (во время фазы Remark) 3-й цикл — (вероятно, Mixed цикл GC) освободит память. - person Martin; 24.10.2019
comment
Возможно, вы имеете в виду очень специфический алгоритм gc. Как правило, «восстановление памяти» не является фактическим действием. Память становится доступной в целом, когда все выжившие объекты перемещены. Нет необходимости рассматривать референта удаленной фантомной ссылки как оставшегося в живых. Таким образом, его память восстанавливается, когда она не копируется. «Это можно сделать за два цикла» не означает, что это делают все алгоритмы. Особенно параллельные сборщики… особенные. - person Holger; 24.10.2019
comment
Да, я просто предположил, что мы говорим о GC G1, который имеет эталонную обработку (старого поколения) в Remark фазе, поэтому в этом случае GC потребуется не менее 3 циклов GC (2 Concurrent Mark цикла и 1 Mixed GC цикл) для восстановления объем памяти. В любом случае, спасибо за такие хорошие ответы! - person Martin; 24.10.2019
comment
@Martin Я не слишком углублялся в журналы, но быстрый тест показывает: GC(0) Pause Young (Normal) (G1 Evacuation Pause), за которым следует GC(0) Skipped phase1 of Reference Processing..., тот факт, что в моей программе нет слабых ссылок на процесс, не имеет значения. Дело в том, что эти журналы отображаются под young pause, где также обрабатываются ссылки, а не только Remark. Фактически, они должны обрабатываться везде, где есть приостановленная фаза. Некоторые GC (Shenandoah) также могут выполнять большую часть обработки на этапе concurrent, - person Eugene; 12.03.2020
comment
главным образом потому, что он сканирует referent, а не reference (это немного сложнее, но идея такова). Из-за этого он может обрабатывать некоторые типы слабых ссылок (при некоторых условиях) в фазе concurrent. - person Eugene; 12.03.2020