skip to content

Phantom References, ReferenceQueue & Cleaner

PhantomReference always returns null from get, because its whole point is a post-mortem notification delivered through a ReferenceQueue after the referent is gone. Cleaner builds on this and is the recommended, finalizer-free way to release native or off-heap resources.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

Compare strong, soft, weak, and phantom references in terms of when the GC clears them and what each is for.

level: juniorimportance: should knowfreq 45%

answer

  1. Strong=never, Soft=memory-pressure, Weak=ASAP, Phantom=post-mortem
  2. Soft -> cache; Weak -> WeakHashMap; Phantom -> cleanup
  3. Phantom get() always null
  4. Weaker tier = collected sooner
  5. Queue optional for soft/weak, mandatory for phantom

basics

~20 s

Strong references are normal references and stop collection. Soft references are cleared only when memory is low (caches). Weak references are cleared as soon as nothing strong points to the object (canonical maps). Phantom references are the weakest and only signal that the object was already collected (cleanup).

solid answer

~40 s

Java has four reference strengths. A strong reference is the ordinary kind — while one exists, the object is never collected. A SoftReference is cleared only when the JVM needs memory, making it suitable for memory-sensitive caches. A WeakReference is cleared as soon as the object is no longer strongly (or softly) reachable, which is ideal for canonical mappings and WeakHashMap keys where you don't want the map to pin entries. A PhantomReference is the weakest: get() always returns null, and it exists solely so the GC can enqueue it after the object is collected, signalling that it's safe to release associated native resources. Each weaker tier means the GC reclaims the object sooner. Soft/weak references may use a ReferenceQueue; phantom references must, since the queue is their only output.

go deeper

for a junior

Can name the four strengths and roughly when each is collected (strong never, phantom last).

for a middle

Maps each type to its use case (soft=cache, weak=WeakHashMap, phantom=cleanup) and knows phantom get() is null.

for a senior

Explains reachability ordering, the ReferenceQueue role per type, and why soft fits caching where weak doesn't.

for a principal

Reasons about GC interaction with each tier under load, cache-policy tradeoffs, and when to reach for phantom/Cleaner vs deterministic close.

## Reachability: the foundation The garbage collector keeps an object alive based on how it can be **reached** from the running program's roots (live thread stacks, statics). Java defines four **reference strengths**, from strongest to weakest. The strongest reachability path to an object decides its fate. ## 1. Strong reference (the default) Any ordinary variable or field — `Foo f = new Foo();` — is a **strong reference**. While *any* strong reference chain reaches an object, the GC will **never** collect it. This is what you use 99% of the time. Memory leaks usually come from accidental strong references (e.g. a static list that keeps growing). ## 2. SoftReference — memory-sensitive `SoftReference<T>` (in `java.lang.ref`) lets the GC keep the object **as long as memory allows**. The object is cleared **only when the JVM is under memory pressure** and about to throw `OutOfMemoryError`. `get()` returns the referent until then, or null afterwards. - **Use:** memory-sensitive **caches** — values that are nice to keep but can be recomputed if reclaimed. - **Caveat:** behavior depends on heap size and GC; not a precise cache policy. ## 3. WeakReference — collected eagerly `WeakReference<T>` is cleared **as soon as the object is no longer strongly or softly reachable** — typically at the very next GC. `get()` returns the referent or null once cleared. - **Use:** **canonicalizing maps** and `WeakHashMap` (keys that shouldn't keep entries alive), listener registries, metadata side-tables that must not pin the subject object. - **Contrast with soft:** weak doesn't wait for memory pressure; it lets go promptly. ## 4. PhantomReference — post-mortem only `PhantomReference<T>` is the **weakest**. `get()` **always returns null** by design, so you can never reach or resurrect the referent. Its only role is to be **enqueued onto a ReferenceQueue after the object has been collected**, signalling that it's safe to release **native/off-heap resources**. - **Use:** deterministic, finalizer-free cleanup — directly, or via `Cleaner` which wraps it. - It must be created with a ReferenceQueue (otherwise it's useless). ## The ReferenceQueue link All three non-strong types may be paired with a `ReferenceQueue`; after the GC clears the reference, it **enqueues** it so your code can react. For soft/weak this is optional (you can poll `get()` for null); for phantom it's the **only** signal. ## Ordering during collection Within one GC pass the order is: clear weakly-reachable-only objects, then softly, then enqueue phantoms (after any finalization). The practical mental model: **strong = never collected; soft = collected under memory pressure; weak = collected ASAP; phantom = already collected, here's your cleanup ping.** ## Summary table | Type | get() | Cleared when | Typical use | |---|---|---|---| | Strong | n/a | never (while reachable) | normal code | | Soft | referent/null | under memory pressure | memory-sensitive cache | | Weak | referent/null | next GC once not strong/soft | WeakHashMap, canonical maps | | Phantom | always null | n/a (enqueued post-collection) | native-resource cleanup / Cleaner | ## Key takeaways - Four strengths; weaker = reclaimed sooner. - Soft = cache (memory-pressure), Weak = don't-pin (eager), Phantom = cleanup signal (null get). - ReferenceQueue is optional for soft/weak, mandatory for phantom.

  • Why is a WeakReference a poor choice for a memory cache but a SoftReference better?
    WeakReferences are cleared at the next GC as soon as nothing strong points to the value, so cache entries vanish almost immediately, giving poor hit rates. SoftReferences survive until the JVM actually needs memory, so cached values stay around while there's room — matching cache intent.
  • What is WeakHashMap and why does it use weak references?
    WeakHashMap holds its keys via weak references, so once a key is no longer strongly referenced elsewhere, its entry becomes eligible for removal automatically. This prevents the map from keeping otherwise-dead keys alive — useful for metadata/side tables keyed by objects you don't own.

saying these in an interview costs you the question

  • Swapping soft and weak semantics (soft waits for memory pressure; weak does not).
  • Thinking weak references are good caches — they're cleared too eagerly; soft references fit caching.
  • Believing a phantom reference can return or hold the object.
  • Assuming any non-strong reference guarantees the object stays alive.

context

open as a page

What is a PhantomReference in Java, and why does its get() method always return null?

level: middleimportance: should knowfreq 35%

basics

~20 s

A PhantomReference is the weakest reference type. Its get() always returns null on purpose, so you can never resurrect the object. It only exists to tell you, via a ReferenceQueue, that the object has already been collected so you can run cleanup.

open as a page

What is the java.lang.ref.Cleaner API, and why did it replace Object.finalize()?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Cleaner (Java 9+) is the modern, safe way to run cleanup when an object becomes unreachable. You register an object plus a cleanup action; Cleaner runs it later on a background thread. It replaced finalize() because finalizers were unpredictable, slow, and could resurrect objects or fail silently.

open as a page

How does a ReferenceQueue underpin soft, weak, and phantom reference notification?

level: seniorimportance: should knowfreq 28%

basics

~20 s

A ReferenceQueue is a queue the garbage collector puts reference objects onto after it clears their referent. You register a reference with a queue; later you poll or block on that queue to find out the object was collected, then react (usually cleanup).

open as a page

You own a class wrapping an off-heap buffer. How do you design its resource cleanup using AutoCloseable and a Cleaner backstop, and what are the pitfalls?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

Make the class AutoCloseable so callers free the buffer promptly with try-with-resources. Also register it with a Cleaner as a safety net if they forget. Put the buffer handle in a separate state object the cleanup doesn't tie back to the wrapper, and make close() and the Cleaner path idempotent.

open as a page