Diagnosing and Analyzing Java Heap Memory Exhaustion
JVM Heap Structure Overview
The Java heap is divided into the Young Generation and the Old Generation.
Young Generation
New objects are primarily allocated in the Eden space. When Eden fills up, a Minor GC occurs, moving live objects to one of the Survivor spaces (S0 or S1). Objects that survive multiple cycles are eventually promoted to the Old Generation based on their age threshold.
Old Generation
This area holds objects with long lifecycles. When this space becomes saturated, a Major GC (often part of a Full GC) is tirggered to reclaim memory.
Simulating a Leak
To observe the behavior of a memory leak, consider the following code snippet where objects are continuously added to a collection with out being released:
import java.util.ArrayList;
import java.util.List;
public class HeapExhaustionDemo {
public static void main(String[] args) {
List<byte[]> storageContainer = new ArrayList<>();
while (true) {
// Allocate 1MB chunks continuously
storageContainer.add(new byte[1024 * 1024]);
}
}
}
Running this application will eventually result in a java.lang.OutOfMemoryError: Java heap space.
Monitoring with jvisualvm
Use the built-in jvisualvm tool to monitor the application in real-time.
- Launch the tool via the command line.
- Install the Visual GC plugin to view real-time graphs of Eden, Survivor, and Old Gen usage.
- Observe the heap usage graph; in a leak scenario, the line will trend upwards and fail to drop significantly after GC cycles.
Capturing and Analyzing Heap Dumps
When the application crashes or exhibits high memory usage, capture a snapshot of the heap:
jmap -dump:format=b,file=snapshot.hprof <process_id>
To ensure a dump is generated automatically during a crash withoutt manual intervention, add the following JVM flag:
-XX:+HeapDumpOnOutOfMemoryError
Using MAT (Memory Analyzer Tool)
Open the generated .hprof file using Eclipse MAT. The tool often provides a Leak Suspects report immediately.
- Look for the Dominator Tree or Histogram views.
- Identify objects with the highest retained size. In the simulation above,
byte[]arrays held by theArrayList(referenced asstorageContainer) will dominate the memory. - Examine the GC Roots path to see which reference chain is preventing the garbage collector from reclaiming the memory.
This analysis pinpoints the exact location in the code where the massive allocation occurs, allowing for a targeted fix.