TT Lab
Get started
Learn Learning paths Courses

The heap had room, but the service stopped

Find the culprit in a heap dump

Continue in TT Lab

Goal

Create an OutOfMemoryError yourself, find the culprit with a heap dump and a class histogram, and leave the startup options that prevent recurrence in a script.

Why it matters

A dump is created only at the moment of the incident. If you do not turn the option on, there is nothing after the incident. And an OutOfMemoryError does not kill the process — ExitOnOutOfMemoryError ends the state of being half dead and passing the health check. The material Leak.java keeps putting arrays of size -Dleak.kb=256 into a static map (it does not put them in with -Dleak.retain=false), sets the speed with -Dleak.sleep.ms, and the number of iterations with -Dleak.iterations. It prints max_heap_mb= at start.

Steps

  1. Compile Leak.java in /root/jvm/heap and run it with java -Xmx64m -cp /root/jvm/heap Leak > /root/jvm/heap/oom.log 2>&1. The log must contain java.lang.OutOfMemoryError: Java heap space.

  2. Run it again with -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/root/jvm/heap/oom.hprof added, and save the output to /root/jvm/heap/oom-dump.log. oom.hprof must be created (1MB or more, starting with JAVA PROFILE 1.0.2) and the log must contain Heap dump file created.

  3. Start java -Xmx64m -Dleak.sleep.ms=50 -cp /root/jvm/heap Leak in the background and after 2 seconds save jcmd <pid> GC.class_histogram > /root/jvm/heap/histo.txt and jcmd <pid> GC.heap_dump /root/jvm/heap/live.hprof (you may kill the process afterward).

  4. From the first-ranked line in histo.txt, read the class name and instance count and write them to /root/jvm/heap/top.txt as two lines, top_class=<이름> (the class name) and instances=<n>.

  5. Run the same allocation without references with java -Xmx64m -Dleak.retain=false -Dleak.iterations=3000 -cp /root/jvm/heap Leak > /root/jvm/heap/noleak.log 2>&1. The log must contain done iterations=3000 and no OutOfMemoryError.

  6. Run it with -XX:+ExitOnOutOfMemoryError added, save the output to /root/jvm/heap/exit-on-oom.log and the exit code (echo $?) to /root/jvm/heap/exit.txt. The log must contain Terminating due to java.lang.OutOfMemoryError and exit.txt must be 3.

  7. From the output of java -XX:+PrintFlagsFinal -version, read the MaxHeapSize value and the value with -XX:MaxRAMPercentage=50 added, and write them to /root/jvm/heap/ram.txt as two lines, default_max_heap=<바이트> and half_max_heap=<바이트> (the placeholder being a number of bytes).

  8. Create /root/jvm/heap/start.sh: a script that runs Leak with -Xmx64m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/root/jvm/heap/dumps -XX:+ExitOnOutOfMemoryError -Xlog:gc:file=/root/jvm/heap/gc.log and writes the exit code to /root/jvm/heap/runbook-exit.txt. When you run it, a .hprof must be created in /root/jvm/heap/dumps/, gc.log must be created, and runbook-exit.txt must be 3.

Notes

Create an OutOfMemoryError

Compile Leak.java in /root/jvm/heap, run it with -Xmx64m, and save the output to /root/jvm/heap/oom.log. It must contain java.lang.OutOfMemoryError: Java heap space.

After javac -d /root/jvm/heap /opt/lab/fixtures/jvm/Leak.java, run java -Xmx64m -cp /root/jvm/heap Leak > oom.log 2>&1. The heap fills within a few seconds.

A dump at the moment of the incident

Run it with -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/root/jvm/heap/oom.hprof added and save the output to /root/jvm/heap/oom-dump.log. There must be oom.hprof (1MB or more, starting with JAVA PROFILE 1.0.2) and Heap dump file created.

The two options come before the class name. The JVM creates the dump at the moment the exception is thrown — Dumping heap to ... is printed in the log.

Histogram and dump of a live process

Start Leak in the background with -Xmx64m -Dleak.sleep.ms=50 and after 2 seconds save jcmd GC.class_histogram > /root/jvm/heap/histo.txt and jcmd GC.heap_dump /root/jvm/heap/live.hprof.

After java ... & PID=$!, sleep 2 and call the two jcmd commands. heap_dump first requests a Full GC, so only reachable objects remain.

Read the first line of the histogram

From the first-ranked line in histo.txt, write the class name and instance count to /root/jvm/heap/top.txt as top_class= and instances=.

The first-ranked line starts with ' 1:' and the columns are rank, instances, bytes, and class name. The name is up to before (java.base@…). awk '$1=="1:"{print $2, $4}' gives the two values.

The same allocation, only without references

Run it with -Xmx64m -Dleak.retain=false -Dleak.iterations=3000 and save the output to /root/jvm/heap/noleak.log. It must contain done iterations=3000 and no OutOfMemoryError.

It allocates 3000 × 256KB = 750MB but runs to the end on a 64MB heap. That is because GC reclaims it the moment you let go.

Do not create a zombie

Run it with -XX:+ExitOnOutOfMemoryError added, and save the output to /root/jvm/heap/exit-on-oom.log and the exit code to /root/jvm/heap/exit.txt. It must contain Terminating due to java.lang.OutOfMemoryError and exit.txt must be 3.

java ... > exit-on-oom.log 2>&1; echo $? > exit.txt. At the first OutOfMemoryError the JVM ends immediately, and the exit code is 3 (measured).

Measure the container heap default

Write the MaxHeapSize of java -XX:+PrintFlagsFinal -version and the MaxHeapSize with -XX:MaxRAMPercentage=50 to /root/jvm/heap/ram.txt as default_max_heap= and half_max_heap=.

Extract the values with the awk in the Notes section, and for the second, put -XX:MaxRAMPercentage=50 before -XX:+PrintFlagsFinal. The default is 25%, so the second value is about double.

Nail the options down in a script

Create /root/jvm/heap/start.sh: it runs Leak with -Xmx64m, HeapDumpOnOutOfMemoryError, HeapDumpPath=/root/jvm/heap/dumps, ExitOnOutOfMemoryError, and -Xlog:gc:file=/root/jvm/heap/gc.log, and writes the exit code to /root/jvm/heap/runbook-exit.txt. When run, it creates a .hprof in dumps/, gc.log, and runbook-exit.txt (3).

If you give a directory to HeapDumpPath, the file is created as java_pid.hprof — put mkdir -p on the script's first line. The last line is echo $? > runbook-exit.txt.