Gymterview
middle

Что такое Strong, Weak, Soft и Phantom ссылки в Java

Типы ссылок в Java — это механизм из пакета java.lang.ref, определяющий, как сборщик мусора обрабатывает объект. Существует четыре уровня силы ссылок: Strong, Soft, Weak и Phantom. Чем слабее ссылка, тем легче GC собирает объект.

Аналогия из жизни: представьте, что вы держите предмет (Strong — крепко в руке, не отдадите), лежит в кармане (Soft — отдадите, только если руки заняты и нужно место), прикреплён стикером (Weak — слетит при любом дуновении ветра), или вы видите только след от предмета (Phantom — предмет уже забрали, но вы можете узнать, что его забрали).

Сравнительная таблица

Тип get() Когда собирается ReferenceQueue Применение
Strong Никогда (пока есть ссылка) Обычный код
Soft Объект или null При нехватке памяти Опционально Кэши
Weak Объект или null При ближайшем GC Опционально WeakHashMap, listeners
Phantom Всегда null После finalization Обязательно Очистка ресурсов, замена finalize

Strong Reference (сильная ссылка)

Обычная ссылка в Java. Объект не будет собран GC, пока на него существует хотя бы одна strong reference.

Пример
Object obj = new Object(); // strong reference
obj = null;                // теперь объект доступен для GC

Soft Reference (мягкая ссылка)

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

Пример
SoftReference<BufferedImage> ref = new SoftReference<>(loadImage(path));
BufferedImage image = ref.get(); // объект или null, если GC собрал

Weak Reference (слабая ссылка)

Объект будет собран при ближайшем цикле GC, если нет strong или soft ссылок. Используется для ассоциативных структур.

Пример
WeakReference<ExpensiveObject> weakRef = new WeakReference<>(new ExpensiveObject());
ExpensiveObject obj = weakRef.get();
if (obj != null) {
    obj.doSomething();
}

WeakHashMap — готовая реализация Map, использующая weak references для ключей. Записи автоматически удаляются, когда на ключ нет strong references.

Phantom Reference (фантомная ссылка)

Самая слабая ссылка. get() всегда возвращает null. Используется вместе с ReferenceQueue для получения уведомления о том, что объект финализирован. Основное применение — очистка внешних ресурсов.

Пример
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantomRef = new PhantomReference<>(resource, queue);
// phantomRef.get() всегда null
// После GC: phantomRef помещается в queue для обработки

С Java 9 механизм Cleaner (основанный на PhantomReference) полностью заменил deprecated finalize():

Пример использования Cleaner
public class NativeResource implements AutoCloseable {
    private static final Cleaner CLEANER = Cleaner.create();

    private final long nativePtr;
    private final Cleaner.Cleanable cleanable;

    public NativeResource() {
        this.nativePtr = allocateNative();
        // CleanAction НЕ должен ссылаться на NativeResource
        this.cleanable = CLEANER.register(this, new CleanAction(nativePtr));
    }

    @Override
    public void close() {
        cleanable.clean(); // явная очистка
    }

    private static class CleanAction implements Runnable {
        private final long ptr;
        CleanAction(long ptr) { this.ptr = ptr; }

        @Override
        public void run() {
            freeNative(ptr);
        }
    }
}

Порядок сборки GC

Пример
Strong -> Soft -> Weak -> Phantom -> Сборка памяти

1. Есть Strong reference -> объект НЕ собирается
2. Только Soft references -> собирается при нехватке памяти
3. Только Weak references -> собирается при ближайшем GC
4. Только Phantom references -> объект финализирован,
   PhantomReference помещается в ReferenceQueue

Частые ошибки

  • Использование SoftReference вместо WeakReference (или наоборот) — разное поведение GC
  • Хранение strong reference на объект рядом с weak/soft reference — GC не соберёт объект
  • Забывают проверять ref.get() != null — объект может быть собран в любой момент
  • Использование PhantomReference без ReferenceQueue — бессмысленно
  • Действие очистки в Cleaner ссылается на очищаемый объект — создаёт strong reference, объект никогда не будет собран

На собеседовании: назовите четыре типа ссылок и когда GC собирает каждый. Ключевые примеры: Soft — для кэшей, Weak — для WeakHashMap и listeners, Phantom — замена finalize через Cleaner. Частая ловушка — вопрос о PhantomReference.get() (всегда null) и о том, зачем нужен ReferenceQueue.