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:
topreports 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 useprstaton Solaris orpslist -don 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>.
Last reviewed on Sat Feb 01 2025 00:00:00 GMT+0000 (Coordinated Universal Time)