Перейти к основному содержимому

Фантомные ссылки в Java

· 3 мин. чтения

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.

Мы узнали, что это такое и как они могут быть полезны на нескольких простых и конкретных примерах.