by BehindJava

How to know which Java thread is hogging the CPU

Home » java » How to know which Java thread is hogging the CPU

In this tutorial we are going to learn about which Java thread is hogging the CPU.

Identifying which Java Thread is consuming most CPU in production server.

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 geek skills.

A Test Application

In order to test this, we need a test application. So I will give you one. It consists of 3 classes:

  • A HeavyThread class that does something CPU intensive (computing MD5 hashes)
  • A LightThread class that does something not-so-cpu-intensive (counting and sleeping).
  • A StartThreads class to start 1 cpu intensive and several light threads.

Here is code for these classes:

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

/**
 * thread that does some heavy lifting
 */
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 stuff 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 the data
                        digest.update(data.getBytes());
                }
        }
}

import java.util.Random;

public class LightThread implements Runnable {

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

        @Override
        public void run() {
                Long l = 0l;
                while(true) {
                        l++;
                        try {
                                Thread.sleep(new Random().nextInt(10));
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }
                        if(l == Long.MAX_VALUE) {
                                l = 0l;
                        }
                }
        }
}

public class StartThreads {

        public static void main(String[] args) {
                // lets start 1 heavy ...
                new HeavyThread(1000);

                // ... and 3 light threads
                new LightThread();
                new LightThread();
                new LightThread();
        }
}

Assuming that you have never seen this code, and all you have a PID of a runaway Java process that is running these classes and is consuming 100% CPU.

First let’s start the StartThreads class and get the PID from task manager as shown below. pid

And I have a java process with PID 12860. Lets get the stack dump of this process using jstack:

C:\Users\cldee>jstack 12860
2022-09-24 11:20:38
Full thread dump Java HotSpot(TM) 64-Bit Server VM (16.0.1+9-24 mixed mode, sharing):

Threads class SMR info:
_java_thread_list=0x000001e056e44250, length=16, elements={
0x000001e055fab8a0, 0x000001e055fac9e0, 0x000001e055fbce60, 0x000001e055fc0880,
0x000001e055fc1280, 0x000001e055fc1c80, 0x000001e055fc2a60, 0x000001e055fdfae0,
0x000001e0560408a0, 0x000001e056c9ebc0, 0x000001e056ca13e0, 0x000001e056ca6140,
0x000001e056ca7ab0, 0x000001e056ca7fd0, 0x000001e056ca84f0, 0x000001e0388377e0
}

"Reference Handler" #2 daemon prio=10 os_prio=2 cpu=0.00ms elapsed=1013.65s tid=0x000001e055fab8a0 nid=0x23e8 waiting on condition  [0x000000770aaff000]
   java.lang.Thread.State: RUNNABLE
        at java.lang.ref.Reference.waitForReferencePendingList(java.base@16.0.1/Native Method)
        at java.lang.ref.Reference.processPendingReferences(java.base@16.0.1/Reference.java:243)
        at java.lang.ref.Reference$ReferenceHandler.run(java.base@16.0.1/Reference.java:215)

"Finalizer" #3 daemon prio=8 os_prio=1 cpu=0.00ms elapsed=1013.65s tid=0x000001e055fac9e0 nid=0x2ad8 in Object.wait()  [0x000000770abff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(java.base@16.0.1/Native Method)
        - waiting on <0x0000000081201308> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(java.base@16.0.1/ReferenceQueue.java:155)
        - locked <0x0000000081201308> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(java.base@16.0.1/ReferenceQueue.java:176)
        at java.lang.ref.Finalizer$FinalizerThread.run(java.base@16.0.1/Finalizer.java:171)

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=1013.62s tid=0x000001e055fbce60 nid=0x37e8 runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Attach Listener" #5 daemon prio=5 os_prio=2 cpu=0.00ms elapsed=1013.62s tid=0x000001e055fc0880 nid=0x247c waiting on condition  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Service Thread" #6 daemon prio=9 os_prio=0 cpu=0.00ms elapsed=1013.62s tid=0x000001e055fc1280 nid=0x1844 runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Monitor Deflation Thread" #7 daemon prio=9 os_prio=0 cpu=0.00ms elapsed=1013.62s tid=0x000001e055fc1c80 nid=0x3738 runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #8 daemon prio=9 os_prio=2 cpu=1031.25ms elapsed=1013.62s tid=0x000001e055fc2a60 nid=0x30ac waiting on condition  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
   No compile task

"C1 CompilerThread0" #10 daemon prio=9 os_prio=2 cpu=187.50ms elapsed=1013.62s tid=0x000001e055fdfae0 nid=0x19f4 waiting on condition  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
   No compile task

"Sweeper thread" #11 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=1013.62s tid=0x000001e0560408a0 nid=0x2194 runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Notification Thread" #12 daemon prio=9 os_prio=0 cpu=0.00ms elapsed=1013.57s tid=0x000001e056c9ebc0 nid=0x1a4 runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Common-Cleaner" #13 daemon prio=8 os_prio=1 cpu=0.00ms elapsed=1013.56s tid=0x000001e056ca13e0 nid=0x3670 in Object.wait()  [0x000000770b5ff000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
        at java.lang.Object.wait(java.base@16.0.1/Native Method)
        - waiting on <0x0000000081200490> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(java.base@16.0.1/ReferenceQueue.java:155)
        - locked <0x0000000081200490> (a java.lang.ref.ReferenceQueue$Lock)
        at jdk.internal.ref.CleanerImpl.run(java.base@16.0.1/CleanerImpl.java:140)
        at java.lang.Thread.run(java.base@16.0.1/Thread.java:831)
        at jdk.internal.misc.InnocuousThread.run(java.base@16.0.1/InnocuousThread.java:134)

"Thread-0" #14 prio=5 os_prio=0 cpu=953625.00ms elapsed=1013.56s tid=0x000001e056ca6140 nid=0x26a4 runnable  [0x000000770b6fe000]
   java.lang.Thread.State: RUNNABLE
        at java8.HeavyThread.run(HeavyThread.java:25)
        at java.lang.Thread.run(java.base@16.0.1/Thread.java:831)

"Thread-1" #15 prio=5 os_prio=0 cpu=281.25ms elapsed=1013.55s tid=0x000001e056ca7ab0 nid=0x8a8 waiting on condition  [0x000000770b7ff000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(java.base@16.0.1/Native Method)
        at java8.LightThread.run(LightThread.java:17)
        at java.lang.Thread.run(java.base@16.0.1/Thread.java:831)

"Thread-2" #16 prio=5 os_prio=0 cpu=140.62ms elapsed=1013.55s tid=0x000001e056ca7fd0 nid=0x18d0 waiting on condition  [0x000000770b8ff000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(java.base@16.0.1/Native Method)
        at java8.LightThread.run(LightThread.java:17)
        at java.lang.Thread.run(java.base@16.0.1/Thread.java:831)

"Thread-3" #17 prio=5 os_prio=0 cpu=328.12ms elapsed=1013.55s tid=0x000001e056ca84f0 nid=0x218c waiting on condition  [0x000000770b9ff000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(java.base@16.0.1/Native Method)
        at java8.LightThread.run(LightThread.java:17)
        at java.lang.Thread.run(java.base@16.0.1/Thread.java:831)

"DestroyJavaVM" #18 prio=5 os_prio=0 cpu=140.62ms elapsed=1013.55s tid=0x000001e0388377e0 nid=0x1a28 waiting on condition  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"VM Thread" os_prio=2 cpu=7765.62ms elapsed=1013.66s tid=0x000001e055fa7c50 nid=0x1068 runnable

"GC Thread#0" os_prio=2 cpu=4625.00ms elapsed=1013.68s tid=0x000001e038876c20 nid=0x17e4 runnable

"GC Thread#1" os_prio=2 cpu=4546.88ms elapsed=1013.12s tid=0x000001e056ddbef0 nid=0x1028 runnable

"GC Thread#2" os_prio=2 cpu=4265.62ms elapsed=1013.12s tid=0x000001e056ddca90 nid=0xd70 runnable

"GC Thread#3" os_prio=2 cpu=4468.75ms elapsed=1013.12s tid=0x000001e056ddcda0 nid=0x2810 runnable

"G1 Main Marker" os_prio=2 cpu=0.00ms elapsed=1013.68s tid=0x000001e038887b20 nid=0x3008 runnable

"G1 Conc#0" os_prio=2 cpu=0.00ms elapsed=1013.68s tid=0x000001e038888ef0 nid=0x2aa8 runnable

"G1 Refine#0" os_prio=2 cpu=0.00ms elapsed=1013.68s tid=0x000001e038904c70 nid=0x15e0 runnable

"G1 Service" os_prio=2 cpu=0.00ms elapsed=1013.68s tid=0x000001e038907710 nid=0x19e8 runnable

"VM Periodic Task Thread" os_prio=2 cpu=31.25ms elapsed=1013.57s tid=0x000001e056ca10f0 nid=0x22f0 waiting on condition

JNI global refs: 14, weak refs: 0

From my top I see that the PID of the top thread is 12860. And 12860 in hex is 0x17e4. Notice that in the stack dump, each thread has an nid which is displayed in hex. And it just so happens that 0x17e4 is the id of the thread and so you can confirm that the thread which is running the HeavyThread class is consuming most CPU.

In read world situations, it will probably be a bunch of threads that consume some portion of CPU and these threads put together will lead to the Java process using 100% CPU.

Summary

  • Run top.
  • Press Shift-H to enable Threads View.
  • Get PID of the thread with highest CPU.
  • Convert PID to HEX.
  • Get stack dump of java process.
  • Look for thread with the matching HEX PID.