1. Обзор
В этой статье мы рассмотрим концепцию фантомной ссылки на языке Java.
2. Фантомные ссылки
Фантомные ссылки имеют два основных отличия от мягких и слабых ссылок.
Мы не можем получить референт фантомной ссылки. Референт никогда не доступен напрямую через API, поэтому нам нужна очередь ссылок для работы с этим типом ссылок.
Сборщик мусора добавляет фантомную ссылку в очередь ссылок после выполнения метода finalize ее референта . Это означает, что экземпляр все еще находится в памяти.
3. Варианты использования
Есть два распространенных варианта использования, для которых они используются.
Первый способ — определить, когда объект был удален из памяти , что помогает планировать задачи, чувствительные к памяти. Например, мы можем дождаться удаления большого объекта, прежде чем загружать другой.
Вторая практика заключается в том, чтобы избегать использования метода финализации
и улучшать процесс финализации .
3.1. Пример
Теперь давайте реализуем второй вариант использования, чтобы практически понять, как работает этот тип ссылок.
Во-первых, нам нужен подкласс класса PhantomReference
для определения метода очистки ресурсов:
public class LargeObjectFinalizer extends PhantomReference<Object> {
public LargeObjectFinalizer(
Object referent, ReferenceQueue<? super Object> q) {
super(referent, q);
}
public void finalizeResources() {
// free resources
System.out.println("clearing ...");
}
}
Теперь мы напишем улучшенную финализацию:
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
List<LargeObjectFinalizer> references = new ArrayList<>();
List<Object> largeObjects = new ArrayList<>();
for (int i = 0; i < 10; ++i) {
Object largeObject = new Object();
largeObjects.add(largeObject);
references.add(new LargeObjectFinalizer(largeObject, referenceQueue));
}
largeObjects = null;
System.gc();
Reference<?> referenceFromQueue;
for (PhantomReference<Object> reference : references) {
System.out.println(reference.isEnqueued());
}
while ((referenceFromQueue = referenceQueue.poll()) != null) {
((LargeObjectFinalizer)referenceFromQueue).finalizeResources();
referenceFromQueue.clear();
}
Сначала мы инициализируем все необходимые объекты: referenceQueue
— для отслеживания поставленных в очередь ссылок, references
— для последующей очистки, largeObjects
— для имитации большой структуры данных.
Далее мы создаем эти объекты, используя классы Object
и LargeObjectFinalizer
.
Прежде чем мы вызовем сборщик мусора, мы вручную освободим большой кусок данных, разыменовав список largeObjects
. Обратите внимание, что мы использовали ярлык для оператора Runtime.getRuntime().gc()
для вызова сборщика мусора.
Важно знать, что System.gc()
не запускает сборку мусора немедленно — это просто подсказка для запуска процесса JVM.
Цикл for
демонстрирует, как убедиться, что все ссылки поставлены в очередь — он выведет true
для каждой ссылки.
Наконец, мы использовали цикл while
для опроса ссылок в очереди и очистки каждой из них.
4. Вывод
В этом кратком руководстве мы представили фантомные ссылки Java.
Мы узнали, что это такое и как они могут быть полезны на нескольких простых и конкретных примерах.