A long-running service's heap keeps growing until OutOfMemoryError — how do you diagnose it?
Reviewed by Gurusankar M.
⚡ Short Answer
Confirm with GC logs (old gen not reclaimed after Full GC), capture a heap dump (jmap / -XX:+HeapDumpOnOutOfMemoryError), analyze dominator tree in Eclipse MAT to find the GC root holding the growing object set — usually an unbounded cache, static collection, or ThreadLocal.
☕Coffee Chat Question
Concept Made Simple
“A long-running service's heap keeps growing until OutOfMemoryError — how do you diagnose it?”
🧠Mind Map Answer
Remember It Faster
Leak vs. under-sized heap — the GC-log test: watch old-generation occupancy across several Full GCs, not just one. A genuinely under-sized heap still shows old-gen dropping back to roughly the same baseline after each Full GC — the app's real working set fits, there's just less headroom than you'd like. A leak shows old-gen occupancy creeping upward Full GC after Full GC, never returning to its previous floor, because something is retaining objects that should have died. If you can't watch it happen live, the same signal is in a heap-dump histogram taken at two points in time: a growing instance count for the same class between the two dumps is the leak's fingerprint.
Shallow vs. retained size in MAT — the distinction that makes the dominator tree useful instead of misleading. Shallow size is just the memory the object itself occupies (its fields, not what they point to) — a HashMap instance's shallow size is tiny regardless of how many entries it holds. Retained size is the shallow size plus every object that would become unreachable (and thus collectible) if this object were removed — for that same HashMap, retained size includes every key, every value, and everything they reference, which is usually where the real memory is. Sorting MAT's dominator tree by retained size is how you find the one object actually responsible for gigabytes of heap, when its own shallow footprint might be under a kilobyte.
WeakHashMap/SoftReference are a self-bounding alternative to a hand-rolled TTL cache, worth knowing as a fix option, not just "add eviction." A WeakHashMap entry is automatically removed once its key has no strong references anywhere else — useful for caches keyed by objects whose lifecycle you don't otherwise control (e.g. a per-Class or per-listener metadata cache), though it's a narrow fit since most caches are keyed by ids/strings that stay strongly reachable elsewhere anyway. SoftReference-wrapped values are the more commonly useful pattern: the GC is permitted (not required) to clear soft references before throwing OutOfMemoryError, so a soft-reference cache tends to self-evict under real memory pressure rather than being the cause of it — the trade-off is that eviction timing is JVM-decided and non-deterministic, which is why an explicit bounded cache (Caffeine, with a real size/TTL policy) is still the recommended default and these are situational tools, not blanket replacements.
Why jmap -dump:live (not a plain -dump) matters for diagnosis, not just file size. The live flag forces a full GC immediately before writing the dump, so only objects that survive that collection — the actual live/reachable set — end up in the .hprof file; garbage that just hasn't been collected yet is excluded. Skipping live on a multi-gigabyte heap can produce a dump dominated by soon-to-be-garbage noise that makes MAT's dominator tree far less useful for finding the real retention culprit, and a needlessly enormous file to boot. The one trade-off: forcing a full GC is itself a real, sometimes multi-second STW pause on a production system — capturing a live dump is a deliberate, disruptive diagnostic step, not something to run casually or on a schedule — schedule it for a maintenance window or a canary instance, not the primary traffic-serving node.
⌨️Hands-on Keyboard
Learn by Doing
# auto-capture on OOM
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dumps
# on demand — jmap (older JDKs)
jmap -dump:live,format=b,file=heap.hprof <pid>
# on demand — jcmd (preferred on modern JDKs; jmap is deprecated for removal)
jcmd <pid> GC.heap_dump /dumps/heap.hprof
# then open heap.hprof in Eclipse MAT -> 'Leak Suspects'🔥What If?
Think Beyond the Expected
GC runs constantly but frees little and CPU is pegged — what's happening?
That's GC thrashing: the live set nearly fills the heap, so Full GCs run back-to-back reclaiming little (the 'GC overhead limit exceeded' precursor). Either the heap is too small or there's a leak retaining objects.
😂Real World
The #1 enterprise Java leak is an unbounded HashMap cache (no eviction/TTL) or a static List that only ever grows. MAT's dominator tree points straight at the retaining GC root.
🎯Interviewer's Expectation
Keywords they're listening for:
⚠️Common Mistakes
- ✗Just bumping -Xmx instead of finding the root cause
- ✗Analyzing without a heap dump (guessing)
- ✗Caches with no max size or eviction policy
✅Best Practices
- ✓Always enable HeapDumpOnOutOfMemoryError in prod
- ✓Bound every cache (size + TTL) — Caffeine/Guava
- ✓Watch old-gen occupancy as an alert signal
🔁Follow-up Questions
- 1How do you tell a leak from just under-sized heap?
- 2What's retained vs shallow size in MAT?
- 3How does a ThreadLocal leak in a thread pool?
🧩Related Technologies
📚References
Continue Learning with AI
Take this question deeper with your favourite AI assistant. Pick a depth, copy the prompt, or open it directly — AI is your learning companion, not a shortcut.
Plain-language foundations
I'm preparing for a software engineering interview and want to understand this from scratch, as a beginner. Topic: Memory/GC (Core Java) Interview question: "A long-running service's heap keeps growing until OutOfMemoryError — how do you diagnose it?" Please: 1. Explain the core idea in simple, plain language, using an everyday analogy. 2. Define any technical terms you use. 3. Walk through one small, concrete example. 4. Finish with a single sentence I can easily remember. Keep the tone friendly and assume I'm new to this topic.
Was this answer helpful?
⭐ Featured Products
Support our platform by exploring our recommended products.
As an Amazon affiliate, purchases through these links may earn us a small commission — at no extra cost to you. It helps keep Full Stack Interview Guru free.