Question 26 · Section 3

What is reachability in the context of GC?

4. Phantom — object almost removed 5. Unreachable — garbage

Language versions: English Russian Ukrainian

Junior Level

Reachability — object status: can it be reached from code.

5 levels:

  1. Strong — normal reference (Object obj = new Object())
  2. Soft — removed when memory is low
  3. Weak — removed at next collection
  4. Phantom — object almost removed
  5. Unreachable — garbage

Middle Level

Reference Types

Type When removed Use case
Strong Never Business objects
Soft On OOM threat Caches
Weak At next GC WeakHashMap
Phantom After finalization Resource cleanup
// Soft Reference
SoftReference<Data> ref = new SoftReference<>(data);
Data cached = ref.get();  // null if GC removed

// Weak Reference
WeakReference<User> ref = new WeakReference<>(user);
// GC removes at nearest collection

// Phantom Reference
PhantomReference<Resource> ref = new PhantomReference<>(resource, queue);
// get() always null! Only for tracking removal

Cleaner (Java 9+)

// Replacement for finalize()
Cleaner cleaner = Cleaner.create();
Cleanable cleanable = cleaner.register(obj, () -> {
    nativeFree(handle);  // Cleanup
    // handle is a native pointer (long), obtained from JNI/MemorySegment.
    // Cleaner allows freeing native memory that GC doesn't control.
});

cleanable.clean();  // Manual cleanup

Senior Level

SoftReference Formula

JVM cleans when:
  current_time - last_access < free_memory × SoftRefLRUPolicyMSPerMB

Default: 1000 ms per each MB
  → "9 hours" — theoretical maximum at 32 GB free and default policy.
  → In practice, SoftReferences are cleaned much earlier under load.

Tuning: -XX:SoftRefLRUPolicyMSPerMB=100

Reference Handler Thread

High-priority system thread:
  1. GC detects reachability change
  2. Object → Pending List
  3. Reference Handler → ReferenceQueue
  4. Application polls queue

→ Asynchronous, doesn't block GC

Resurrection

// ❌ In finalize() you can resurrect an object
protected void finalize() {
    GlobalCache.add(this);  // Strong reference → object alive!
}

// Cleaner cannot resurrect:
// → Lambda does NOT have reference to this

Best Practices

  1. Strong = business logic
  2. Soft = caches (tune SoftRefLRUPolicyMSPerMB)
  3. Weak = metadata
  4. Phantom/Cleaner = native resources
  5. Avoid resurrection
  6. ReferenceQueue for monitoring

Senior Summary

  • 5 levels of reachability
  • Soft = caches, cleanup on OOM threat
  • Weak = next collection
  • Phantom = removal tracking
  • Cleaner = finalize() replacement
  • Reference Handler = async thread
  • Resurrection = architectural evil

Interview Cheat Sheet

Must know:

  • 5 reachability levels: Strong (normal reference), Soft (on low memory), Weak (at next GC), Phantom (after finalization), Unreachable (garbage)
  • SoftReference: for caches; cleanup formula: free_memory × SoftRefLRUPolicyMSPerMB (default 1000 ms/MB); on 32 GB — theoretically 9 hours, but in practice much earlier
  • WeakReference: for WeakHashMap, metadata; cleaned at NEXT collection (doesn’t wait for memory shortage)
  • PhantomReference: get() always null; used ONLY with ReferenceQueue for removal tracking; replaces finalize()
  • Cleaner (Java 9+): finalize() replacement; lambda does NOT have reference to this → no “resurrection”; cleanable.clean() for manual cleanup
  • Reference Handler: high-priority system thread; GC → Pending List → Reference Handler → ReferenceQueue → application polls
  • Resurrection: in finalize() you can assign this to static field → object “resurrects”; Cleaner cannot resurrect

Common follow-up questions:

  • Why can SoftReference on 32 GB live for hours? — Formula: 32 GB × 1000 ms/MB = 9 hours; tuning: -XX:SoftRefLRUPolicyMSPerMB=100 → more aggressive
  • Why does PhantomReference.get() always return null? — Specifically designed: object almost removed, reference no longer needed; only ReferenceQueue for tracking
  • How is Cleaner better than finalize()? — Cleaner lambda does NOT have reference to this → impossible to resurrect object; finalize() can assign this → resurrection
  • Why is Reference Handler asynchronous? — Doesn’t block GC; GC puts object in Pending List; Reference Handler processes later → application polls ReferenceQueue

Red flags (DO NOT say):

  • “SoftReference is cleaned immediately after Strong reference removed” — cleaned ONLY on OOM threat
  • “PhantomReference allows getting the object” — get() always null; only ReferenceQueue
  • “finalize() is a normal way to clean resources” — deprecated (Java 9+), unpredictable, can resurrect object

Related topics:

  • [[5. When does an object become eligible for GC]]
  • [[25. What are GC roots]]
  • [[27. Can you manually invoke GC]]
  • [[6. What is a memory leak in Java]]
  • [[4. What is Garbage Collection]]