Why is my JVM process larger than max heap size?
By tomas.nilsson on Feb 02, 2009
(by Stefan Särne)
A perfectly valid question, and one that is hard to answer without knowing a bit about what goes on under the hood of your JVM (Java Virtual Machine).
This blog entry will give a short description of why this can be the case and also describe the diagnostic command in JRockit called print_memusage, which can be used to ask a running JRockit process how much memory it uses, and for what.
But why larger than -Xmx?
The command line argument -Xmx sets the maximum Java heap size (mx). All java objects are created on the Java heap, but the total memory consumption of the JVM process consist of more things than just the Java heap. A few examples:
- Generated (JIT:ed) code
- Loaded libraries (including jar and class files)
- Control structures for the java heap
- Thread Stacks
- User native memory (malloc:ed in JNI)
It is important to think of this when dimensioning how many processes that should run on a single server and what to set maximum heap size to. Usually the heap is by far the largest part of the process space, but the total size of the process will also depend on your application.
Reserved != Committed
It is easy to be alarmed by the number for reserved (or mapped) memory, but a process will not use any physical memory resources until it memory is committed. JRockit will for example reserve the heap up to maximum heap size, but not use it until needed. It will also, for example, reserve 1 GB for generated code on 64-bits X86 architectures, but only commit as is needed.
JRockit has a Diagnostics Command called print_memusage.
This little tool started with developers in the JRockit engineering team that wanted to understand in detail what the JVM used the memory for. After the development of the first version it became evident that the customers also wanted to know what the JVM uses the memory for, and what it actually means. This brief history is included so that you, as a user, will forgive us for not having a more user friendly tool. It will be improved in the next major release of JRockit (normal disclaimers on release predictions apply).
Memusage is a command that will ask the sub-systems in JRockit how much memory they are using, which in turn will walk through the memory it holds on to and report it. It will also ask the OS how much the process is indeed using and scan the virtual memory space for what type it is. This approach makes it possible to retrieve the information from any running JVM, without any cost until it is invoked. Due to technical limitations, the tool is “good” but not 100% exact.
In this example I use jrcmd to connect to JRockit in my local development environment.
As you can see in this screen shot, the tool reports data hierarchically, starting with “Total mapped”, and then showing details broken down in smaller pieces.
Some things worth highlighting:
The process has reserved/mapped about 1 GB of memory.
Out of that, about 708692 KB is in use, i.e. committed.
Both Total mapped and Total in-use are gathered from the OS.
The process has 28544 KB as executable memory. This includes both native code, like the jvm library itself and code generated (jit:ed) by the jvm. In this example, 21.1% of the memory which is executable, is allocated by the compiler in the JVM, 5652 KB (or 94%) is currently in use. The fact that the total is higher than 100% is not as strange as it may seem.The JVM will pool the code memory and reuse it and only free it when a large enough amount is unused. There are several reason for code not being in use any more, for example if the method is optimized then the first version may no longer be needed, or if the class is unloaded. This is handled by the Code GC in JRockit.
The largest chunk is usually the rw-memory, which includes the Java heap. The Stacks are the thread stacks, one for each thread. Under “classes” and under “malloced memory” is meta information about the java code, which is needed both for the JVM to keep track and for upholding the java model, like debug info when throwing exceptions. It also includes all static Strings, and all jar/zip handlers, which is live when reading from jar files.
If there is a number inside parenthesis, it indicates the number of items of the indicated type has been allocated. In the example above, the #97701 says that there are 97701 method structs.
In unaccounted for is "the rest", in other words the difference between the total in use and what the subsystems are responsible for. It includes some fragmentation in the memory malloced by the JVM, but also memory allocated from JNI code.
So this is a neat little tool to get insight into the process memory. Whoever said that memory is cheap these days? I hope this will serve as a good starting point to understand where all the memory goes.