How to Pinpoint Java Out‑Of‑Memory Errors Using jmap and VisualVM
This guide explains why frequent garbage collection and heap memory overflow occur in Java applications, demonstrates how to reproduce the issue with a sample program, and walks through various techniques—including jmap histograms, heap dumps, VisualVM analysis, and remote JMX monitoring—to diagnose and resolve out‑of‑memory errors.
Introduction
GC (Garbage Collection) frequent and heap memory overflow happen because objects occupy heap space that cannot be reclaimed, preventing allocation of new objects, triggering GC or directly causing OutOfMemoryError, which terminates the process.
The investigation approach is to first examine the size and proportion of different object types in the process, identify objects that occupy large space, and then analyze whether the program misuses them. The following focuses on various ways to view heap memory distribution.
Sample Program
Compile (javac FrequentFullGCSample.java) the sample program and start a JVM process with the following command.
Specify -Xms2M -Xmx2M to limit heap size to 2 MB, sufficient for the example; additionally add -XX:+PrintCommandLineFlags to print changed JVM options and -XX:+PrintGCDetails to print detailed GC information.
<code>java -Xms2M -Xmx2M -XX:+PrintCommandLineFlags -XX:+PrintGCDetails FrequentFullGCSample</code> <code>import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
public class FrequentFullGCSample {
private static final Queue<Object> QUEUE = new LinkedList<>();
public static void main(String[] args) throws InterruptedException {
while (true) {
QUEUE.offer(new Object());
TimeUnit.MILLISECONDS.sleep(3);
}
}
}</code>The console log first shows some changed JVM option values: initial heap (-XX:InitialHeapSize) and maximum heap (-XX:MaxHeapSize) are both 2097152 bytes (2 MB); class pointer compression (-XX:+UseCompressedClassPointers) and object pointer compression (-XX:+UseCompressedOops) are enabled.
Later the GC log shows objects initially allocated in the young generation; when that space fills, frequent Young GC occurs.
After several generations of collection, objects age and are promoted to the old generation; as the old generation fills, frequent Full GC occurs.
Because the program creates objects that are strongly referenced, they cannot be reclaimed, leading to OutOfMemoryError after repeated Full GC fails to free space. The memory usage of the young and old generations approaches 100%.
Inspecting Heap Object Allocation
jmap -histo (recommended)
jmap is a JDK tool that can display heap memory information.
jmap -histo <JavaProcessID>lists each object type’s instance count and memory size in descending order; piping through
head -10shows the top ten entries.
This method works while the process is still running, produces limited output, and has minimal impact on the process.
<code>jmap -histo 20091 | head -10</code>jmap -dump + Java VisualVM
jmap -dumpcan dump heap memory to a binary file:
jmap -dump:format=b,file=<filename> <JavaProcessID>.
This generates a large amount of data; for large heaps (e.g., gigabytes) it takes time and impacts the process, so it is not recommended unless the heap is small.
<code>jmap -dump:format=b,file=71976.dump 71976</code>With the heap dump file, use a tool such as Java VisualVM (bundled with the JDK) to analyze it.
<code># Mac: /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/jvisualvm
# Windows: use jvisualvm.exe in the JDK bin directory</code>In VisualVM, choose File → Load, select the appropriate file type, and open the heap dump.
After loading, click on Classes to see the number of instances and size for each class.
Automatic Heap Dump on Out‑Of‑Memory (recommended)
Add the JVM option
-XX:+HeapDumpOnOutOfMemoryErrorat startup. The dump file is created in the current directory as
java_pid<pid>.hprof; you can specify a directory with
-XX:HeapDumpPath=<dir>. Then open it with VisualVM.
This method is recommended because it captures information right before the process exits, aiding post‑mortem analysis.
<code>java -Xms2M -Xmx2M \
-XX:+PrintCommandLineFlags \
-XX:+PrintGCDetails \
-XX:+HeapDumpOnOutOfMemoryError FrequentFullGCSample</code>Java VisualVM Remote Monitoring
Enable remote monitoring by adding several JVM options: set
-Djava.rmi.server.hostnameto the host IP,
-Dcom.sun.management.jmxremote.portfor the remote port, and disable authentication and SSL.
Remote monitoring adds overhead, so it is usually avoided in production but useful during testing and load testing.
<code>java -Xms2M -Xmx2M -XX:+PrintCommandLineFlags -XX:+PrintGCDetails \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.port=1099 \
-Djava.rmi.server.hostname=192.168.252.132 FrequentFullGCSample</code>In VisualVM, double‑click Remote, enter the host address, and confirm.
Right‑click the newly added host, choose Add JMX Connection, enter the port, and confirm.
Double‑click the JMX connection, then select Sampler → Memory to see real‑time object allocation.
Conclusion
The recommended low‑impact methods are
jmap -histoand automatic heap dumps on Out‑Of‑Memory errors.
Additionally,
jmap -heap <JavaProcessID>can display JVM heap configuration and overall allocation.
Ops Development Stories
Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.