Оператор «Сборщик мусора добавляет фантомную ссылку в очередь ссылок после выполнения метода finalize его референта.» В лучшем случае немного небрежно.
Вы должны обратиться к спецификации:
Если сборщик мусора в определенный момент времени определяет, что референт фантомной ссылки фантомно достижим , то в это время или в более позднее времяон будет ставить ссылку в очередь.
В то время как связанное определение «фантомно достижимых» состояний:
Объект фантомно достижим , если он не является ни тем, ни другимсильно, мягко, или слабо достижимо, оно было завершено, и некоторая фантомная ссылка ссылается на него.
Таким образом, объект фантомно доступен «после выполнения метода finalize его референта», если только на него ссылаютсяпо призрачным ссылкам и, следовательно, будет поставлен в очередь после этого , но не сразу .Поскольку объект является сильно достижимым во время выполнения его finalize()
метода, требуется по крайней мере один дополнительный цикл сбора мусора, чтобы обнаружить, что он стал фантомно достижимым.Затем «в то время или в более позднее время» он будет поставлен в очередь.
Если вы измените программу на
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
Object object = new Object() {
@Override
protected void finalize() throws Throwable {
System.out.println("finalize()");
}
};
PhantomReference<Object> phantomReference = new PhantomReference<>(object, referenceQueue);
object = null;
System.gc();
Thread.sleep(1_000);
System.gc();
Thread.sleep(1_000);
System.out.println("isEnqueued() after GC: " + phantomReference.isEnqueued());
Reference reference = referenceQueue.poll();
if(reference != null) {
System.out.println("isEnqueued() after poll(): " + phantomReference.isEnqueued());
}
Скорее всего, вы увидите желаемый результат, но он имеетПодчеркнем, что нет никаких гарантий, что сборщик мусора действительно будет работать, когда вы вызываете System.gc()
, или что он завершает его определенное количество времени, когда он работает, или что он найдет все недоступные объекты в определенном цикле.Кроме того, постановка в очередь происходит асинхронно после цикла gc, поэтому даже к тому времени, когда сборщик мусора завершит работу и обнаружит особое состояние достижимости, может пройти дополнительное время, прежде чем он будет помещен в очередь.
Обратите внимание, чтопредложение «Это подразумевает, что экземпляр все еще находится в памяти». также не дает правильного представления, но в данном случае оно основано на недоразумении, которое было даже на стороне разработчика ядра Java.
КогдаAPI был создан, в спецификацию было добавлено предложение, которое вы найдете даже в версии для Java 8:
В отличие от мягких и слабых ссылок, фантомные ссылки не очищаются сборщиком мусора автоматически, как онипомещён.Объект, достижимый с помощью фантомных ссылок, будет оставаться таким до тех пор, пока все такие ссылки не будут очищены или сами станут недоступными.
Это может привести к наивному предположению, что объект все еще должен находиться в памяти, но1041 * Спецификация языка Java® гласит:
Могут быть разработаны оптимизирующие преобразования программы, которые уменьшают число достижимых объектов до меньшего, чем те, которые наивно считались бы достижимыми.
Проще говоря, память объектов может быть восстановлена раньше, если поведение программы не изменится.Это особенно относится к сценариям, где приложение вообще не может использовать объект, как в случае с фантомной ссылкой.Поведение программы не изменилось бы, если бы объект больше не находился в памяти, поэтому вы не можете предполагать, что это на самом деле.
Это приводит к вопросу, почему правило, по которому не удаляются фантомные ссылки, былодобавил в спецификации вообще.Как обсуждалось в этом ответе , этот вопрос был поднят и не мог быть дан вообще.Следовательно, это правило было удалено в Java 9, и фантомные ссылки очищаются, когда ставятся в очередь как слабые и мягкие ссылки.Это еще более веская причина не предполагать, что объект все еще находится в памяти, поскольку теперь даже неоптимизирующие среды могут восстановить память объекта в этот момент.