How to Troubleshoot Performance Problems Due to High CPU Utilization by a Java Application Thread


Applies To

Java SE JDK and JRE - Version 7 to 8
Information in this document applies to any platform.


Introduction

This article describes how to troubleshoot performance problems due to high CPU utilization by a thread in a Java application.

Disclaimer: This sample code is provided for educational purposes only. It is not supported by Oracle Support. It has been tested internally, however we do not guarantee that it will work for you. Ensure that you run it in a safe test environment. Additionally, the code and object names used in this article represent fictitious sample names that make up an example. Any similarity to actual code is purely coincidental and not intended in any other manner.


Troubleshooting Steps

Test Program

Copy and run the code below. In this test program, the main thread enters a loop that alternates between a heavy computational phase and an inactive phase where it sleep for a few seconds.

public class BusyCPU {
    static final int PERIOD = 1000000000;
    static final int PAUSE = 2000;
    static volatile double d = 1;
    public static void main(final String[] args) {
        for (;;) {
            long start = System.nanoTime();
            while ((System.nanoTime() - start) < PERIOD) {
                d = Math.random();
            }
            try {
                System.out.println("sleep for 2 secs...");
                Thread.sleep(PAUSE);
                System.out.println("resume...");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

Linux Example

If you run top on the Java process, you can easily identify the thread(s) that take up a lot of CPU time. In this example, it is the thread listed at the top:

% top -H -b -n 1 -p <PID>
PID    USER    PR NI VIRT RES  SHR  S %CPU %MEM   TIME+  COMMAND
<PID> <USER>  20  0 713m 11m  7072 S   72  0.6   1:17.89 java
<PID> <USER>  20  0 713m 11m  7072 S    0  0.6   0:00.02 java

Note: top reports the percentage of recent CPU time used by the process. This represents an average CPU usage over some time interval, where the program could have transitioned to different states in the execution. You may use prstat on Solaris or pslist -d on Windows to get CPU usage details.

The typical next step is to get a thread dump of the same process and check the main thread.

In this example, if you take one sample of a thread dump using jstack, you will get one of the following stack traces depending on what the program is doing at that moment:

A. When the execution is in computation phase:

"main" #1 prio=5 os_prio=0 tid=0x00007f7c10009000 nid=0x177b6d runnable [0x00007f7c1695e000]
    java.lang.Thread.State: RUNNABLE
         at java.util.Random.next(Random.java:204)
         at java.util.Random.nextDouble(Random.java:532)
         at java.lang.Math.random(Math.java:773)
         at BusyCPU.main(BusyCPU.java:10)

B. When the execution is in the sleeping phase:

"main" #1 prio=5 os_prio=0 tid=0x00007f7c10009000 nid=0x177b6d waiting on condition [0x00007f7c1695e000]
    java.lang.Thread.State: TIMED_WAITING (sleeping)
         at java.lang.Thread.sleep(Native Method)
         at BusyCPU.main(BusyCPU.java:14)

If you caught the thread in phase B (sleeping), you would have a hard time figuring out why it took up high CPU load.

Therefore, a better approach is to take better sampling of thread dumps (7 in this example), using a sample shell script such as this (pid is the process id of JVM):

#! /bin/sh
    pid=$1
    if test ! -z "$pid"
    then
        for loop in 1 2 3 4 5 6 7
        do
            top -H -b -n 1 -p $pid 1 1 > top.out.$loop
            jstack $pid > jstack.out.$loop
        done
    else
        echo "Usage: $0 pid"
    fi

The script generates 14 files (top.out.1, top.out.2, …, top.out.7 and jstack.out.1, jstack.out.2, …, jstack.out.7).

With this approach, you should be able to capture the various states of the running thread, and be able to correlate the CPU usage with the different states in the execution cycle. First find the top.out.X file where the CPU usage is high, and then examine the corresponding jstack.out.X.

In the above BusyCPU example, you should catch the process spending CPU cycles in the inner loop running Math.random(), and find a way to avoid the performance problem by fixing the programming logic.


Solaris Example

Similar to Linux but using the prstat command instead of top:

% prstat -Lp <PID>
PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU PROCESS
<PID> <NAME>    106M   27M sleep   20    0   0:00:56  19% java
<PID> <NAME>    106M   27M sleep   59    0   0:00:00 0.0% java
<PID> <NAME>    106M   27M sleep   59    0   0:00:00 0.0% java

Here’s an equivalent data sampling batch script for the Solaris platform (PID is process ID of JVM):

#! /bin/sh
    pid=$1
    if test ! -z "$pid"
    then
        for loop in 1 2 3 4 5 6 7
        do
            prstat -L -p $pid 1 1 > prstat.out.$loop
            jstack $pid > jstack.out.$loop
        done
    else
        echo "Usage: $0 pid"
    fi

Windows Example Using pslist

On Windows you can also use the pslist command. For usage on pslist, refer to the Windows SysInternals page on PsList.

% pslist -d <PID>
    Tid Pri Cswtch   State User Time    Kernel Time  Elapsed Time
   ...
   <TID>   8 250365 Running 0:15:19.656  0:00:00.171  0:46:33.187

The thread ID can be found in column Tid and is printed in decimal.

jstack example output:

"main" prio=6 tid=<TID> nid=<NID> runnable [0x0090f000]

Here the corresponding thread id is called nid, the native thread ID. The nid is printed in hexadecimal.

Here’s an equivalent data sampling batch script for the Windows platform (PID is process ID of JVM):

@echo off
    set PID=%1
    if "%1"=="" goto usage
    if not "%2"=="" goto work
    for %%A in (1 2 3 4 5 6 7) do call %0 %PID% %%A
    goto end
    :work
    set LOOP=%2
    pslist -d %PID% > pslist.out.%LOOP% 2> nul
    jstack %PID% > jstack.out.%LOOP% 2> nul
    goto end
    :usage
    echo Usage: %0 pid
    :end

On Windows, the batch generates 14 files (pslist.out.1, pslist.out.2, …, pslist.out.7 and jstack.out.1, jstack.out.2, …, jstack.out.7).


Windows Example Using PowerShell

In Windows PowerShell you can use the performance counters class with the Get-WmiObject command.

This is a sampling batch script for Windows PowerShell:

param($javapid)
    if ($javapid) {
        for($i = 1; $i -lt 8; $i++) {
            $filter = 'IDProcess = ' + $javapid;
            jstack $javapid > jstack.out.$i
            (Get-WmiObject Win32_PerfFormattedData_PerfProc_Process  -filter $filter |
                Select-Object -Property Name, PercentProcessorTime ) |
            Out-File -FilePath .\proctime.out.$i
            $oldcpu = $cpu
        }
    } else {
        write-host "Usage: %0 -javapid pid"
    }

The batch generates 14 files (proctime.out.1, proctime.out.2, …, proctime.out.7 and jstack.out.1, jstack.out.2, …, jstack.out.7).

To get the PID of the java process, you can use Get-Process java, where the PID is in the Id column.

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
    -------  ------    -----      -----     ------     --  -- -----------
        385      24   865664      16144     912.72  <PID>   1 java

If you need to bypass ExecutionPolicy to run the script, you can run it with powershell -ExecutionPolicy Bypass -File samplescript.ps1 -javapid <PID>.

See also How to Obtain a Thread Dump (Stack Traces) From a Java Process or Core File of a Java Process on Windows.


Last reviewed on Sat Feb 01 2025 00:00:00 GMT+0000 (Coordinated Universal Time)