How to profile threads in Java 5

On the previous page, we gave a general introduction to thread profiling methodology. Now it's time to put things into practice. Recall that the basic thing that we want to do is sit in a loop, periodically asking the JVM what all the threads are doing. After getting the result (in the form of a series of stack traces), we need to somehow assimilate this information, so that over time we get some kind of percentage breakdown of where threads spend their time.

Introducing ThreadMXBean

To query the JVM for thread information, we resort to the ThreadMXBean class. This class is part of the Java Management and Instrumentation framework included as standard in Java 5. We won't get bogged down in the details of this framework here, except to note that various other instrumentation "beans" are available as standard, notably the MemoryPoolMXBean which allows us to monitor usage of individual heaps within the JVM, and GarbageCollectorMXBean, which allows us to track overall time spend garbage collecting (arguably more granularity would actually have been useful in this last case).

The management and instrumentation classes live inside the java.lang.management package. Another class inside this package, ManagementFactory, provides us with an entry point for obtaining management beans, in our case ThreadMXBean:

import java.lang.management.*;
...
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();

Now we have our ThreadMXBean, we can start asking it for some interesting information. Two very important methods to us will be:

long[] getAllThreadIds();
ThreadInfo[] getThreadInfo(long[] ids, int maxDepth);

The first of these methods returns an array containing all the internal IDs of threads that are "live" at the point of calling the method. We use these internal IDs to refer to the threads in other calls to ThreadMXBean, notably the second of the above calls. The getThreadInfo() method returns us an array of ThreadInfo objects, each of which encapsulates the state (including the current stack trace) of the corresponding ID in the ids array passed in.

The maxDepth parameter allows us to specify the maximum lines of stack trace to return for any one thread. The higher the number, the less chance that we'll "miss" some information, but the more work we're potentially asking the JVM to do. Tuning this parameter depends on our purpose. We may just want to know the top entry, in which case we can pass in a value of 1. This is the simplest case, but may not give us useful information in all cases. For example, if a thread is currently inside BufferedInputStream.read(), that doesn't tell us which bit of our code is calling the read method. So in a moment, we'll consider a more complex case where we want to know the topmost entry in the stack trace that refers to a line in our code, as opposed to the Java class libraries.

Now we have the basic calls we need, we're ready to get some measurements by putting getThreadInfo() in a loop.


If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.

Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.