Last edited: Weds. April 7, 2010
Corrections or questions: bryanesmith at gmail dot com
The purpose of this guide is to help a new Java developer troubleshoot memory problems, such as performance degradation or OutOfMemoryError
issues in Java 6. (Some of these tools might be available for previous and subsequent versions, but I am basing this document on my experience with Java 6.)
I also intend for this document to be used as a quick reference guide.
In a long-living Java process, performance degradation is often due to a memory leak. This happens because the JVM spends increasingly more time performing garbage collection.
Eventually, an OutOfMemoryError occurs. I have seen these in two scenarios:
(* There are JVM options to tune this sort of behavior; however, this default value is reasonable in most cases considering that the process is mostly unproductive.)
The keys to successfully troubleshooting a memory issue is to generate a heap dump and seeing what is taking up so much memory.
As of Java 6, you can set a runtime JVM flag to generate a heap dump on an OutOfMemoryError: -XX:+HeapDumpOnOutOfMemoryError
. If you are running the application from command-line:
If you are running from an IDE, you should add this flag as a JVM parameter.
If an OutOfMemoryError occurs, a file will be generated with the name java_pid[pid].hprof in the working directory for the process. E.g., if the process has a pid of 4432, then the file would be called java_pid4432.hprof.
This is useful when a Java process does not have the JVM flag set to generate a heap on OOM error or an OOM error never occurs but performance is degrading. Note that the jmap utility comes with the JRE 6 installation. (jmap is available for Java 5, but has different command-line parameters. Type the command without any parameters for usage information.)
The command to generate the heap dump:
You need to know your process id. In Windows, you can find it using the task manager. In Unix, I use ps -Af | grep -i java
from the command-line:
In this example, the pid is 3565. So the command would be:
You can use jhat, which comes with the JRE 6 installation. To run it from command line:
Note that jhat requires a lot of heap space, so you offer it as much as you can when working with large heap dumps. E.g., to offer 1 GB of heap space to jhat:
Once jhat has loaded your heap dump, you can browse the heap histogram, as well as object references, in a web browser. By default, the process listens on port 7000, so you would simply visit http://localhost:7000
.
Two other options:
In the next session, I will discuss why you might need an alternative to jhat such as MAT when working with large heap dumps.
One of our servers ran out of heap space, java.lang.OutOfMemoryError: Java heap space
. I used jmap to generate the output before restarting the process, and I downloaded the heap dump from the server to my laptop.
The heap dump is 982MB. My laptop has a little more than 2GB of free memory, and so I first attempted to open the heap dump using jhat. But even when I allocated 2GB of space to jhat, it would eventually fail with an OOM error.
I then tried IBM HeapAnalyzer with the same result as jhat.
Next, I tried MAT. The first attempt failed after a long time. So I opened MemoryAnalyzer.ini
in the MAT directory and set the maximum heap space to 2GB, and the tool succeeded in opening the heap dump in under a minute.
MAT generates indices to help manage the heap information. Apparently, it buffers less in memory, allowing it to succeed where the other tools did not.
Once the tool was running, I generated a heap histograph and found the problem immediately.
Two classes of objects were using well over 90% of all memory. One of the classes was only for troubleshooting and wasn't being used, so I quickly removed references to it in the code an the problem was solved.
Usually OOM errors take more troubleshooting than this, but the process is similar. Note that jhat is very useful, but if you have problems with a large heap dump, then the Eclipse Memory Analyzer (MAT) might be the tool you need.
jmap -histo [pid]