Pages

Identifying which Java thread is consuming the most CPU

I didn’t come up with this. I was shown how to do this by an esteemed college at work. I only tested this on Linux.


Introduction

Most (if not all) productive systems doing anything important will use more than 1 java thread. And when something goes crazy and your cpu usage is on 100%, it is hard to identify which thread(s) is/are causing this. Or so I thought. Until someone smarter than me showed me how it can be done. And here I will show you how to do it and you too can amaze your family and friends with your mad geek skillz.

 

A Test application

I have created a test application that you can also download/git-clone here:

https://github.com/srasul/java-thread-cpu 

The test consists of several classes that include a thread that intentionally does a lot of work (aka uses cpu) and another threat that intentionally does very little. And ofcourse a main class that that runs it all. This application is to simulate a real-world application.

HeavyThread

This is the class that does some "heavy" work:

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

/**
* thread that does some heavy lifting
*
* @author srasul
*
*/
public class HeavyThread implements Runnable {
private long length;

public HeavyThread(long length) {
this.length = length;
new Thread(this).start();
}

@Override
public void run() {
while (true) {
String data = "";

// make some shit up
for (int i = 0; i < length; i++) {
data += UUID.randomUUID().toString();
}

MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}

// hash that shit
digest.update(data.getBytes());
}
}
}

LightThread

This is the class that does some "light" work. We do this so that we can differentiate threads within a running a jvm

import java.util.Random;

/**
* thread that does little work. just count & sleep
*
* @author srasul
*/
public class LightThread implements Runnable {

public LightThread() {
new Thread(this).start();
}

@Override
public void run() {
while (true) {
try {
Thread.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}

}
}
}


StartThreads

This class starts the 1 heavy thread and 3 light threads and also prints out the PID of the currently running JVM

/**
* Hello world!
*/
public class StartThreads {
public static void main(String[] args) {
// lets start 1 heavy ...
new HeavyThread(1000);
System.out.println("started HeavyThread");

// ... and 3 light threads
new LightThread();
new LightThread();
new LightThread();
System.out.println("started 3 LightThread");

System.out.println("Current Java Process PID: " + ProcessHandle.current().pid());
}
}

 

Running this app

To run this, we can use the following maven command:

❯ mvn compile exec:java -Dexec.mainClass="StartThreads"
 

And Finally...

We now come to the point to identify the actual thread

First of all, notice that PID of the java process as printed by the StartThread class and the PID of the process in top that is using the most CPU:


 

Notice that our sample java application is indeed using up 100% of the CPU.

On a live system, this is your starting point: top and a running jvm.

First thing we do is enable "Threads view" in top. You can do this by pressing 'H' (shift-h) in top. Here is an excerpt from the top man page:

          H  :Threads-mode toggle
              When this toggle is On, individual threads will be
              displayed for all processes in all visible task windows.
              Otherwise, top displays a summation of all threads in each
              process.

 When we press H in top for our example, we will see something like this:

 

Notice that the PID is now the thread ID. And in the example above it is: 21298. Let's keep a note of this.

Next, let us get a thread-dump of the running jvm. There are several ways we can do this: sending a kill -3 to the jvm process. Or using the jstack application that comes with the java sdk.

Since we know the PID of the jvm process, we will use this:


Now if we explore the output of jstack we will see our threads that are running:


Remember the top with Threads view? There we saw that the PID of the thread with the highest CPU usage was: 21298. And here can also see the that same thread and we can see from the stack trace that it is indeed the HeavyThread that is using the most CPU.


This trick also works with older jvms, but with one caveat: in the stack dump of older jvms, the thread ID is in HEX. So you will need to convert from hex to binary in order to link the PID in top's Threads view and the thread's 'nid' in the thread dump.

TL;DR

  1. run top
  2. press H to enable Threads view
  3. note the PID of the thread that using up all the CPU
  4. get a thread dump of the running jvm process
  5. match the PID of the thread with the ID or nid of the thread in the dump 
  6. nid of the thread may be in HEX, so convert it to binary if needed.