Вопрос 26 · Раздел 3

Что такое reachability в контексте GC?

4. Phantom — объект почти удалён 5. Unreachable — мусор

Версии по языкам: English Russian Ukrainian

🟢 Junior Level

Reachability (Достижимость) — статус объекта: можно ли до него добраться из кода.

5 уровней:

  1. Strong — обычная ссылка (Object obj = new Object())
  2. Soft — удаляется при нехватке памяти
  3. Weak — удаляется при следующей сборке
  4. Phantom — объект почти удалён
  5. Unreachable — мусор

🟡 Middle Level

Типы ссылок

Тип Когда удаляется Применение
Strong Никогда Бизнес-объекты
Soft При угрозе OOM Кэши
Weak При следующей GC WeakHashMap
Phantom После финализации Очистка ресурсов
// Soft Reference
SoftReference<Data> ref = new SoftReference<>(data);
Data cached = ref.get();  // null, если GC удалил

// Weak Reference
WeakReference<User> ref = new WeakReference<>(user);
// GC удалит при ближайшей сборке

// Phantom Reference
PhantomReference<Resource> ref = new PhantomReference<>(resource, queue);
// get() всегда null! Только для отслеживания удаления

Cleaner (Java 9+)

// Замена finalize()
Cleaner cleaner = Cleaner.create();
Cleanable cleanable = cleaner.register(obj, () -> {
    nativeFree(handle);  // Очистка
    // handle — это native pointer (long), полученный из JNI/MemorySegment.
    // Cleaner позволяет освободить native-память, которую GC не контролирует.
});

cleanable.clean();  // Ручная очистка

🔴 Senior Level

SoftReference формула

JVM очищает, когда:
  текущее_время - last_access < свободная_память × SoftRefLRUPolicyMSPerMB

По умолчанию: 1000 мс на каждый МБ
  → «9 часов» — теоретический максимум при 32 ГБ free и default policy.
  → В реальности SoftReferences очищаются гораздо раньше под нагрузкой.

Настройка: -XX:SoftRefLRUPolicyMSPerMB=100

Reference Handler Thread

Высокоприоритетный системный поток:
  1. GC обнаруживает изменение достижимости
  2. Объект → Pending List
  3. Reference Handler → ReferenceQueue
  4. Приложение опрашивает очередь
  
→ Асинхронно, не блокирует GC

Resurrection (Воскрешение)

// ❌ В finalize() можно воскресить объект
protected void finalize() {
    GlobalCache.add(this);  // Strong ссылка → объект жив!
}

// Cleaner не может воскресить:
// → Лямбда НЕ имеет ссылки на this

Best Practices

  1. Strong = бизнес-логика
  2. Soft = кэши (настройте SoftRefLRUPolicyMSPerMB)
  3. Weak = метаданные
  4. Phantom/Cleaner = нативные ресурсы
  5. Избегайте resurrection
  6. ReferenceQueue для мониторинга

Резюме для Senior

  • 5 уровней достижимости
  • Soft = кэши, очистка при OOM угрозе
  • Weak = следующая сборка
  • Phantom = отслеживание удаления
  • Cleaner = замена finalize()
  • Reference Handler = асинхронный поток
  • Resurrection = архитектурное зло

🎯 Шпаргалка для интервью

Обязательно знать:

  • 5 уровней достижимости: Strong (обычная ссылка), Soft (при нехватке памяти), Weak (при следующей GC), Phantom (после финализации), Unreachable (мусор)
  • SoftReference: для кэшей; формула очистки: free_memory × SoftRefLRUPolicyMSPerMB (по умолчанию 1000 мс/МБ); на 32 ГБ — теоретически 9 часов, но на практике гораздо раньше
  • WeakReference: для WeakHashMap, метаданных; очищается при СЛЕДУЮЩЕЙ сборке (не ждёт нехватки памяти)
  • PhantomReference: get() всегда null; используется ТОЛЬКО с ReferenceQueue для отслеживания удаления; заменяет finalize()
  • Cleaner (Java 9+): замена finalize(); лямбда НЕ имеет ссылки на this → нет «воскрешения»; cleanable.clean() для ручной очистки
  • Reference Handler: высокоприоритетный системный поток; GC → Pending List → Reference Handler → ReferenceQueue → приложение опрашивает
  • Resurrection: в finalize() можно присвоить this статическому полю → объект «воскреснет»; Cleaner не может воскресить

Частые уточняющие вопросы:

  • Почему SoftReference на 32 ГБ может жить часами? — Формула: 32 ГБ × 1000 мс/МБ = 9 часов; настройка: -XX:SoftRefLRUPolicyMSPerMB=100 → агрессивнее
  • Почему PhantomReference.get() всегда null? — Специально designed: объект почти удалён, ссылка уже не нужна; только ReferenceQueue для отслеживания
  • Чем Cleaner лучше finalize()? — Лямбда Cleaner НЕ имеет ссылки на this → невозможно воскресить объект; finalize() может присвоить this → resurrection
  • Почему Reference Handler асинхронный? — Не блокирует GC; GC помещает объект в Pending List; Reference Handler обрабатывает позже → приложение опрашивает ReferenceQueue

Красные флаги (НЕ говорить):

  • «SoftReference очищается сразу после удаления Strong ссылки» — очищается ТОЛЬКО при угрозе OOM
  • «PhantomReference позволяет получить объект» — get() всегда null; только ReferenceQueue
  • «finalize() — нормальный способ очистки ресурсов» — deprecated (Java 9+), непредсказуем, может воскресить объект

Связанные темы:

  • [[5. Когда объект становится кандидатом на удаление GC]]
  • [[25. Что такое GC roots]]
  • [[27. Можно ли вручную вызвать GC]]
  • [[6. Что такое утечка памяти в Java]]
  • [[4. Что такое Garbage Collection]]