Питання 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]]