Discovering Command-line Java Profiling Tools
At my current client I am responsible for administering a third-party Java appplication that unfortunately generates a lot of performance complaints. It’s a multi-tiered system with the middle-tier running on Oracle Appication Server 10.1.3. I am already familiar with GUI profiling tools like jconsole and jvisualvm, both of which are included with Oracle’s JDK, and I have played around with the profiling capabilities in NetBeans. All of these profiling tools are fairly similar to one another in look and functionality.
Using any one of these tools, I can view the size and utilization of the heap, see the status of running threads, monitor CPU loads, pull garbage collection statistics, and generate a memory dump. All of these are adequate for monitoring application performance in real-time, but while working with the application vendor to diagnose problems I found it necessary to be able to report on application performance over longer spans of time, say days or even weeks. We were trying to show patterns of eroding performance, or possibly spot instances of very slow memory leaks in parts of the application that got used less frequently. In any event, it required a way to monitor all of these details without having to have my eyes on the screen the whole time.
My goal was to get all that data exposed by the aforementioned GUI tools into a database that I could report off of. A little research on the internet (HT to Ted Neward) and some digging around in man pages provided the solution.
Recent versions of the JDK come packaged with some nifty command line tools to assist with profiling your Java applications. They can all be found in the
/bin directory under the JDK base directory.
The jps command is a lot like the ps command in Unix/Linux, in that it lists running processes. Jps, however, will only list discoverable Java processes. If a running Java process is not included in the list returned by jps, you may still be able to profile it; it just doesn’t advertise itself as attachable.
Each Java process is identified by the “VMID” returned by jps, usually corresponding to the process ID on the host system. It is this VMID which is required as an argument to all of the other tools we will discuss, so be sure to always use jps to correctly identify your processes to the other tools.
Use jstack to generate a thread dump for a running Java process. It gives you a nice snapshot of thread activity comparable to what you might get in one of the GUI tools mentioned above. You get the name of the thread, the current state, a stack trace, and with the
-l option you get additional information about locking conditions. If an actual deadlock is detected you get a detailed breakdown of which threads are holding the locks! For comprehending the overall state of threads in an application it can actually prove superior to some of its GUI counterparts, as it’s faster to scan the results; i.e. no need to click from thread to thread or drill down to view the details.
Jstack can be used in conjunction with jmap and jhat to get a more comprehensive picture of application state at a critical point in time.
Note: If you have a 64-bit JDK, you’ll want to use the
-J-d64 flag when running the command.
jmap & jhat
These two tools work together to help you analyze the contents of the heap. Use jmap to generate a heap dump, and jhat to help analyze the dump file.
Jhat works by spawning an HTTP server that presents the contents of the heap as a web page, allowing you to drill down on a specific object to view its members and references. In order to get the most out of jhat, however, it would be worth investigating OQL, the Object Query Language, as the web page generated by jhat allows you to run OQL queries against the heap. If you know a little about your application class structure, this makes your heap analysis far more efficient. Thankfully, the jhat web page provides you with a short primer.
Jmap is good for more than just generating heap dumps, however. When used with the
-heap option it gives you a nicely formatted heap usage summary.
Now I was getting closer to my goal. In fact, this was originally the output I parsed during my first attempt at statistics gathering. Unfortunately, what I found while running on an RHEL 4.7 box with JDK 1.6.0_30 was that jmap would from time to time inexplicably refuse to attach to my Java process, leaving gaping holes in my report. I noticed similar complaints online, so I figured there may be problems with it.
# jmap -J-d64 -heap 11028 Attaching to process ID 11028, please wait... Error attaching to process: sun.jvm.hotspot.debugger.DebuggerException: Can't attach to the process
Note: Once again, be sure to use the
-J-d64 flag with jmap if you’re running a 64-bit JVM.
Jstat was what saved the day for me. Jstat displays detailed heap usage, class loader, and garbage collection statistics for the JVM; basically everything I wanted to report on. Jstat has numerous options that allow you to focus in on usage statistics for different parts of the JVM.
Statistics on the behavior of the class loader.
Statistics of the behavior of the HotSpot Just-in-Time compiler.
Statistics of the behavior of the garbage collected heap.
Statistics of the capacities of the generations and their corresponding spaces.
Summary of garbage collection statistics (same as -gcutil), with the cause of the last and current (if applicable) garbage collection events.
Statistics of the behavior of the new generation.
Statistics of the sizes of the new generations and its corresponding spaces.
Statistics of the behavior of the old and permanent generations.
Statistics of the sizes of the old generation.
Statistics of the sizes of the permanent generation.
Summary of garbage collection statistics.
HotSpot compilation method statistics.
Like the heap profiling features of the GUI tools mentioned earlier, the results of jstat can be used to check for memory leaks or to better tune your JVM startup parameters. Like the vmstat command, jstat can be set to repeat at a specified interval, for an infinite or specific number of iterations. Since jstat has so many options and output formats, I will examine it in more detail in a later blog post when I discuss how I used it to populate my application instrumentation table.
One thing that should be noted is that all of these tools are documented as being “unsupported” and experimental. Oracle make no guarantees that they will be included in future versions of the JDK or that their output or arguments will not change. Still, though, they’ve been around for a while and since they’re there, why not make use of them? Just don’t build any critical production processes off of them if you want total flexibility in taking future JDK upgrades.
In my next post I’ll examine how I used these tools to automate the process of instrumenting my third-party Java app. I’ll also provide you with a script I wrote that formats the output of jstat to look something like the
-heap option of jmap, providing an equivalent, but more reliable, solution.