Что такое 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.