memory management
advancedMemory Leaks & Reference Types
Recognize how unintended references prevent GC, and use Strong / Soft / Weak / Phantom references to control object lifetime.
Java frees memory for you, and Java programs still run out of it.
Strong = a leash you're holding. Soft = a leash on a hook (cut only if the room is full). Weak = a sticky note (gone with the next cleaning). Phantom = a notification when the object is being thrown out.
Key Concepts
1
A leak here does not mean the collector missed something. It means your program is still holding a
reference to an object it will never use again, so the collector is not allowed to free it.
2
The four kinds of reference, and what each tells the collector.
Strong an ordinary field or variable
"never collect this"
Soft "collect this if memory gets tight"
Weak "collect this as soon as nothing strong points at it"
Phantom "tell me after this has been collected"3
You use strong references without thinking — every = creates one. The other three exist for caching and
cleanup.
=
4
A cache wants soft references: keep entries while there is room, drop them under pressure. A lookup
table keyed by objects you do not own wants weak references, so entries disappear when the rest of the
program forgets the key. WeakHashMap does exactly that.
WeakHashMap
5
The leaks that actually happen.A collection that only grows. A static map used as a cache, with nothing ever removed. This is the most
common Java leak by a distance.
static
6
Listeners nobody removes. You register a callback, the publisher stores a strong reference, and your
object can never be collected. Every object that ever registered stays alive.
7
Inner classes holding their parent. A non-static inner class keeps a hidden reference to the object that
created it. A long-lived Runnable defined inside a large object keeps that entire object alive.
Runnable
8
ThreadLocal in a thread pool. This one surprises people. The value is attached to the thread, and a
pooled thread lives forever, so the value is never released:
ThreadLocal
9
try {
context.set(userContext);
handleRequest();
} finally {
context.remove(); // without this it leaks, and the next
} // request may see the previous user's data10
How to find one. Do not watch memory usage — it rises and falls normally. Watch the level it returns to
after a full collection. In a healthy application that floor is steady; in a leaking one it climbs over
hours.
11
Then take two heap snapshots an hour apart under load and compare them. Look for the class whose instance
count grew, then find what is holding it.
12
What the interviewer is probing.1. "Can Java leak memory?" Probing: what the collector does and does not do. Stalls: "No, there is
a garbage collector." Moves up: it frees unreachable objects; a leak is an object still reachable and
never used again.
13
2. "Give a real example." Probing: experience. Stalls: "Not closing resources." Moves up: an
unbounded static cache, a listener never removed, or a ThreadLocal on a pooled thread — with why the
reference survives.
ThreadLocal
14
3. "When would you use a weak reference?" Probing: purpose. Stalls: "To help the collector."
Moves up: for data keyed by an object you do not own, so it disappears with the key. Caches want soft,
not weak.
15
4. "How would you find one?" Probing: method. Stalls: "Read the code." Moves up: the
post-collection floor, then two heap snapshots compared.
16
5. "Why is ThreadLocal risky in a pool?" Probing: lifetime mismatch. Stalls: "It is per-thread,
so it is fine." Moves up: pooled threads are reused forever, so the value is never released and can leak
between requests.
ThreadLocal
17
Reference strengths, and what each is for18
Type Collected when Cleared by GC? Use for
------------------------------------------------------------------
Strong never while reachable no ordinary code
Soft memory pressure last resort memory-sensitive
caches
Weak next GC after strong eagerly metadata keyed
refs are gone by foreign
objects
Phantom already finalised; always deterministic
get() always returns native cleanup
null (replaces
finalize)19
Typical leak shapes and the fix20
static Map cache = new HashMap<>(); -> bounded cache with
eviction (Caffeine),
or WeakHashMap keys21
publisher.addListener(this); -> removeListener in the
matching teardown22
ThreadLocal<Ctx> ctx = ... -> try { ... }
(in a pooled thread) finally { ctx.remove(); }23
new Runnable(){...} inside a big object -> static nested class, or
a lambda that captures
only what it needs