At work, I recently wrote up a page for our internal wiki about Java memory problems and how to debug them. It’s quite simplified and short as it’s geared towards beginning or intermediate Java developers. Nevertheless it might be useful to someone out there.
We first look at the most essential Java memory basics, then see how we can track down out-of-memory problems. I’ve thrown in the most useful references.
Java memory basics
This section is over-simplified to keep it simple.
Objects are placed on a memory region called the heap. The size of the heap depends on the Java parameters
-Xms for the initial size, and
-Xmx for the maximum size; for example,
Objects are not needed anymore when there are no more references to them; they are unreachable. The Java virtual machine will clean these up and free the memory they occupied. This happens when the heap is starting to fill up, or otherwise in somewhat regular intervals, but not directly after an object becomes obsolete. Careful: This means that when you look at how much memory Java is using (see below for how to do that), the figure is only valid right after garbage collection. Otherwise, old objects are still using memory.
When the heap is full, but all objects on it are still needed, you run out of memory and the virtual machine gives up and throws a
java.lang.OutOfMemoryError. There is also a variant of this exception that says “GC overhead limit exceeded”. This means that there’s a tiny bit of memory left, but garbage collection can free so little of it that it needs to kick in so soon after its last run finished that most of the program’s time is spent in garbage collection.
You have a memory leak when you create objects that are never garbage collected, even after you wouldn’t need them anymore. You recognize a memory leak when your memory usage goes up and up while the load stays the same.
Usually, this happens when you put objects into a data structure which you keep using, such as a Map, and don’t delete or overwrite objects in that data structure once you don’t need them anymore.. You might not see any reference to your object in your code, but the data structure needs one to track its members. For instance, when using a Map as a cache to look up objects by ID, the cache will grow continuously as the Map will hold a reference to each object ever stored in it, so the garbage collection can never release anything you put in.
Tracking down memory problems
There are two principal ways of examining the memory usage of a Java process. You can connect to the running process to observe its behavior and collect some statistics. And you can create a heap dump, which is basically the complete heap at a certain point in time written out to a file, and analyse it with tools. The latter is the only way to know for sure what objects are using how much memory.
Examining a running Java process
We’ll stick to one tool here, jconsole. There are many more out there, but jconsole is easy, versatile and part of the standard Java SDK. It connects to a running Java process via JMX.
Start it and choose your java process. The “Memory” tab shows the heap usage; right-click to save the numbers as CSV. Click “Perform GC” to run garbage collection before you start an operation you want to test, so your tests always start at the same base line. The “VM Summary” and “MBeans” tabs have all kinds of information. You can also create a heap dump, see the section below.
For analysing heap dumps you can use the Eclipse Memory Analyzer (MAT). Get the stand-alone download as the Eclipse integration is buggy as of 2010-06.
There are several ways to obtain heap dumps. You should always run your Java processes with the
-XX:+HeapDumpOnOutOfMemoryError so you can examine what ate all your memory, should this happen.
You can get a heap dump from a running process directly from MAT via “Acquire heap dump”. jconsole can do it, too: in the MBeans tab, select com.sun.management->HotSpotDiagnostic->Operations->dumpHeap, and click the dumpHeap button.
MAT is quite self-explanatory. Run the “Leak suspects” report and examine the candidates.
Optimizing memory usage and performance
This is a complex topic, and lots could (and has been) written on it. For now, here are just some references and notes.
jstat utility is part of the Java SDK. It prints out statistics on the console. You can use it like this:
jstat -gc <PID> 30s to print out garbage collection statistics of the Java process with every 30 seconds.
- Java Tuning White Paper
- Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning
- Java theory and practice: Garbage collection and performance (very good article)