JavaSeniorTechnical

Что такое soft, weak и phantom references в Java?

SoftReference удерживает объект до нехватки памяти (кеши), WeakReference не мешает GC (WeakHashMap, listeners), PhantomReference позволяет выполнить cleanup после финализации — объект уже недостижим, но ещё не освобождён.

Иерархия ссылок в Java

Все четыре типа находятся в пакете java.lang.ref. Сильная (strong) ссылка — обычная переменная; три остальных типа позволяют GC собирать объекты в разных сценариях.

SoftReference

GC собирает soft-объекты только при нехватке памяти (перед OutOfMemoryError). Идеальны для memory-sensitive кешей.

import java.lang.ref.SoftReference;

SoftReference<byte[]> cache = new SoftReference<>(new byte[1024 * 1024]);

byte[] data = cache.get(); // null, если GC уже собрал
if (data == null) {
    data = loadFromDisk();
    cache = new SoftReference<>(data);
}

WeakReference

GC собирает weak-объекты при первой возможности, если на них нет сильных ссылок. Применяются в WeakHashMap, кешах метаданных, listener-реестрах без утечки памяти.

import java.lang.ref.WeakReference;

WeakReference<ExpensiveObject> weakRef = new WeakReference<>(new ExpensiveObject());

// После следующего GC weakRef.get() может вернуть null
ExpensiveObject obj = weakRef.get();
if (obj != null) {
    obj.doWork();
}

Пример реестра слушателей без утечки:

Map<Listener, Boolean> listeners = new WeakHashMap<>();
listeners.put(myListener, Boolean.TRUE);
// Когда myListener не имеет других сильных ссылок, запись автоматически удаляется

PhantomReference

PhantomReference.get() всегда возвращает null. Объект помещается в ReferenceQueue после финализации, но до освобождения памяти. Используется для детерминированного cleanup ресурсов (альтернатива finalize()).

import java.lang.ref.*;

ReferenceQueue<Resource> queue = new ReferenceQueue<>();
Resource resource = new Resource();
PhantomReference<Resource> phantom = new PhantomReference<>(resource, queue);

resource = null; // убираем сильную ссылку

// В отдельном потоке мониторинга:
Reference<?> ref = queue.remove(); // блокирует до постановки в очередь
if (ref == phantom) {
    cleanupNativeResources();
}

Сравнение поведения

  • Strong: объект живёт, пока есть хоть одна сильная ссылка.
  • Soft: GC собирает при -Xmx pressure; JVM гарантирует очистку до OOM.
  • Weak: GC собирает в ближайший цикл без strong-ссылок.
  • Phantom: get() == null всегда; уведомление через ReferenceQueue после финализации.

Подводные камни

  • SoftReference и производительность: JVM по умолчанию держит soft-ссылки до 1 секунды на каждый МБ свободной кучи (-XX:SoftRefLRUPolicyMSPerMB). На серверах с большой кучей кеш может занимать гигабайты.
  • WeakHashMap и ключи: ключи WeakHashMap слабо ссылаются, но значения — сильно. Если значение ссылается на ключ, утечка памяти не устраняется.
  • Финализаторы и PhantomReference: если у объекта есть finalize(), он может «воскреснуть» (resurrect), создав новую сильную ссылку. PhantomReference ставится в очередь уже после финализации.
  • ReferenceQueue обязателен для PhantomReference: создание PhantomReference без очереди бессмысленно — get() всегда null, и отслеживать освобождение невозможно.
  • Не используйте finalize(): метод устарел в Java 9, удалён в Java 18+. Для cleanup используйте Cleaner (Java 9+) поверх PhantomReference.
  • Cleaner API как правильная альтернатива: java.lang.ref.Cleaner инкапсулирует PhantomReference + ReferenceQueue и запускает действие в выделенном потоке.
  • Производительность GC: большое количество Reference-объектов замедляет GC — он вынужден обрабатывать reference queues на каждом цикле.

Common mistakes

  • Путать термин «reference types» с соседним механизмом Java.
  • Не называть границу lifecycle, transaction, thread или request для «reference types».
  • Игнорировать production-эффекты «reference types»: latency, SQL shape, memory, security или observability.

What the interviewer is testing

  • Попросить объяснить механизм «reference types» на минимальном примере.
  • Проверить, видит ли кандидат failure mode и диагностику для «reference types».
  • Уточнить, какие настройки или API меняют «reference types» в реальном сервисе.

Sources

Related topics