Heap is the memory space where java application objects are stored. Unfortunately the size of Heap is limited (by the -Xmx java command line option). When all of heap is used up, you are in deep trouble. So, it helps to understand how to measure Java heap usage, and that’s exactly what I’m going to be discussing with you in this post.
Very surprisingly, measuring heap is not that straight forward in java world. I believe it is due to the fact there are numerous ways to measure java heap. Oracle seems to introduce new tools with every release (and retire the old ones). For this reason, I recommend everyone to invest in a commercial grade APM (Application Performance Management) tool. With APM tool, you will simply login to the APM interface (typically a web application) to view the JVM heap usage (along with tons of other useful metrics)
Using the command jcmd to measure heap usage
Java comes with a command line tool named jcmd. It should be available in most flavors of Java (Oracle, IBM etc). I’m going to recommend jcmd to measure your heap usage. One of the advantages of using jcmd is the class histogram which not only shows the heap usage but it breaks down the usage by class. This gives you an instant view of what is causing heap to fill up.
Important Note: jcmd does have some impact on the jvm. But I assure it is worth the price, as the details shown by jcmd are very valuable.
Important Note: the command jcmd must be run as the same user (or effective user) as the user running the java application
Let us see jcmd in action.
I’m using Java 1.8 for this excercise. I’m running a java application in my system named test. It is a simple java program (named test.java) that sleeps for 5 minutes.
Simply invoking jcmd without any options shows the available java processes. In the example below, test is my application. sun.tools.jcmd.JCmd is jcmd itself.
~$jcmd 58161 test 58165 sun.tools.jcmd.JCmd
Print all available commands for a given java application
~$jcmd test help 58161: The following commands are available: JFR.stop JFR.start JFR.dump JFR.check VM.native_memory VM.check_commercial_features VM.unlock_commercial_features ManagementAgent.stop ManagementAgent.start_local ManagementAgent.start GC.rotate_log Thread.print GC.class_stats GC.class_histogram GC.heap_dump GC.run_finalization GC.run VM.uptime VM.flags VM.system_properties VM.command_line VM.version help For more information about a specific command use 'help <command>'.
Note: You can specify either the process id (pid) or the main class name of the java application in place of the first parameter to the jcmd command. The syntax of jcmd is jcmd <process id|main class name> <command>.
For measuring the heap usage, you use the command ‘GC.class_histogram’.
~$jcmd test GC.class_histogram 58161: num #instances #bytes class name ---------------------------------------------- 1: 1020 93864 [C 2: 481 54856 java.lang.Class 3: 526 26072 [Ljava.lang.Object; 4: 13 25664 [B 5: 1008 24192 java.lang.String 6: 79 5688 java.lang.reflect.Field 7: 256 4096 java.lang.Integer 8: 94 3760 java.lang.ref.SoftReference 9: 91 3712 [I 10: 111 3552 java.util.Hashtable$Entry 11: 8 3008 java.lang.Thread 12: 60 2200 [Ljava.lang.String; 13: 38 1824 sun.util.locale.LocaleObjectCache$CacheEntry 14: 53 1696 java.util.concurrent.ConcurrentHashMap$Node 15: 23 1472 java.net.URL 16: 14 1120 java.lang.reflect.Constructor 17: 35 1120 java.util.HashMap$Node ... ... 189: 1 16 sun.misc.Unsafe 190: 1 16 sun.net.www.protocol.file.Handler 191: 1 16 sun.reflect.ReflectionFactory Total 4546 283608
In the above output, first column is the serial number, second column is the number of instances, third column is the size in bytes and fourth column is the class name. The last line shows the total. So, there are 4546 instances occupying ~280KB memory.
Now, let’s say I have written some damaging code that creates an integer array of 100,000,000 integers.
System.out.println("I'm creating an array of first 1000000 odd numbers..."); int odd_nums[] = new int[100000000]; int k = 1; for(int i =0;i<100000000;i++) { odd_nums[i] = k; k = k + 2; }
In java each integer takes up 4 bytes. 100,000,000 integers approximately take up 400MB. Once I run the above code, let’s see how much memory is eaten by my application
/$jcmd test GC.class_histogram 58619: num #instances #bytes class name ---------------------------------------------- 1: 91 400003584 [I 2: 1018 93544 [C 3: 481 54856 java.lang.Class 4: 524 25896 [Ljava.lang.Object; 5: 10 25024 [B 6: 1006 24144 java.lang.String 7: 79 5688 java.lang.reflect.Field 8: 256 4096 java.lang.Integer 9: 94 3760 java.lang.ref.SoftReference ... ... 178: 1 16 java.util.concurrent.atomic.AtomicBoolean 179: 1 16 java.util.jar.JavaUtilJarAccessImpl 180: 1 16 java.util.zip.ZipFile$1 181: 1 16 sun.misc.Launcher 182: 1 16 sun.misc.Launcher$Factory 183: 1 16 sun.misc.Perf 184: 1 16 sun.misc.Unsafe 185: 1 16 sun.net.www.protocol.file.Handler 186: 1 16 sun.reflect.ReflectionFactory Total 4514 400281400
As you can see, the heap usage is around 400MB. And the output clearly shows the primitive integer type is taking up all the memory.
Note that jcmd is just one of the many ways you can get the heap utilization info. Here are some more methods
- Verbose GC logs
- Visual VM
- Jconsole
- Jmap
- From Java Heap dump
So, now you know how to measure heap usage of your java application. jcmd is an easy and effective tool to quickly retrieve the heap usage and top classes eating up Heap. While there is some impact to the JVM, jcmd is worth the try while troubleshooting memory issues.
PS: I have an interesting article titled 5 not so easy ways to monitor the Heap Usage of your Java Application. Check it out.