Heap Dump Analysis

Heap dump analysis examines a snapshot of JVM heap state to find retained objects, unexpected object populations, reference chains, and possible memory leaks. For a Java backend engineer, the key task is connecting heap evidence to the allocation or retention behavior that caused memory pressure.

A heap dump is a snapshot of objects and their relationships in the JVM heap at a particular point in time. It can be captured when diagnosing high heap usage, frequent garbage collection, or an `OutOfMemoryError`, then inspected with tools such as Eclipse MAT, VisualVM, or commercial profilers.

A useful analysis starts with retained memory rather than simply counting objects. An object may occupy little space itself but keep a large object graph reachable, so its retained heap can be much larger than its shallow heap. Dominator analysis helps identify objects that effectively retain large portions of the heap.

Reference paths explain why an object remains reachable. Trace suspicious objects toward GC roots such as static fields, active threads, JNI references, or other runtime roots. A suspected leak usually involves objects that should have become unreachable but remain strongly reachable through a long-lived reference.

Object histograms are useful for spotting abnormal populations, such as millions of cache entries, request objects, strings, or collections. Compare suspicious populations with application behavior and, when possible, with dumps from different points in time; a single dump shows state, while multiple observations can reveal growth.

Heap analysis should be combined with GC logs, allocation profiling, application metrics, and source-code inspection. A dump can show what is retained, but determining why those objects were created or whether their lifetime is intentional often requires evidence outside the dump.

Key points

Interview questions

What does a dominator tree tell you during heap analysis?

A dominator tree organizes objects according to which objects control the reachability of other objects. If an object dominates a large subtree, removing that object from the graph would make much of that subtree eligible for collection, assuming no other retaining path exists. This makes dominator analysis useful for finding major retainers rather than merely finding large individual objects. It provides a practical way to prioritize investigation in a large heap.

What is a heap dump, and when would you use one?

A heap dump is a snapshot of objects currently represented in the JVM heap and their relationships. I would use it when investigating problems such as unexplained heap growth, excessive garbage collection, or an `OutOfMemoryError`. I would inspect object counts, retained memory, and reference paths to determine what is keeping suspicious objects reachable. I would then correlate the findings with application code and runtime metrics.

What is the difference between shallow heap and retained heap?

Shallow heap is the memory directly occupied by an individual object. Retained heap represents the memory that would become eligible for collection if that object were removed, subject to the rest of the reference graph. Therefore, a small collection or wrapper can have a large retained heap if it uniquely keeps many other objects reachable. Retained size is especially useful when prioritizing suspected memory-retention problems.

How would you investigate a suspected memory leak from a heap dump?

I would first identify unusually large object populations or objects with high retained heap. Then I would inspect their incoming reference paths and trace them toward GC roots. I would determine whether the retaining reference is intentional, such as a bounded cache, or accidental, such as an ever-growing map or listener registry. Finally, I would validate the hypothesis using GC behavior, application metrics, and source code, ideally comparing heap state across multiple observations.

A heap dump shows millions of strings and map entries. What would you investigate next?

I would first determine which data structures own those objects and whether their population is expected. I would inspect the dominant retaining paths to see whether a cache, static field, session structure, or other long-lived component keeps them reachable. I would then check whether the collection has an intended bound or expiration policy and compare the population with application traffic and configuration. The important distinction is between a legitimately large workload and unbounded retention.