查找cpu占用率最高的java线程

本文讲的是:在 linux 环境下,定位 cpu 使用率最高的 java 线程,找出对应代码的方法。基本思路是:使用 top 命令找出 cpu 占用率最高的 java 线程,然后结合 jstack 定位问题代码。

测试代码

下面是一段测试代码:在 main 方法中启动一个名字为 busyThread 的线程,该线程执行了一个无意义的死循环,用来占用 cpu 资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Top {
public static void main(String[] args) throws InterruptedException {
Thread busyThread = new Thread(new Busy(), "busyThread");

System.out.println("start busy thread");
busyThread.start();

System.out.println("wait for busy thread");
busyThread.join();
}

private static class Busy implements Runnable {
@Override
public void run() {
int i = 0;
while (true) {
i++;
}
}
}
}

使用 javac 命令编译为字节码,然后使用 java 命令执行。此时 busyThread 线程会几乎将 cpu 占满。

查找过程

找出cpu占用率最高的java进程

使用 top 命令查看进程情况,根据 command 列显示的名字判断启动的 java 进程。

  1. 输入 top
  2. 依次输入 xb 打开高亮模式
  3. 使用 <> 切换排序列,切换到按 cpu 排序
1
2
3
4
5
6
7
8
9
10
top - 23:33:06 up 25 min,  2 users,  load average: 1.00, 0.81, 0.53
Tasks: 117 total, 1 running, 66 sleeping, 0 stopped, 0 zombie
%Cpu(s): 25.3 us, 0.2 sy, 0.0 ni, 74.4 id, 0.1 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 948304 total, 651808 free, 121844 used, 174652 buff/cache
KiB Swap: 102396 total, 102396 free, 0 used. 755488 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1283 gorden5+ 20 0 305312 13084 9056 S 99.0 1.4 3:03.99 java
1341 gorden5+ 20 0 8316 3172 2704 R 0.7 0.3 0:00.39 top
......

可以看到 cpu 占用率最高的 java 进程的 ID 为 1283

输出堆栈信息

使用 jstack 命令输出 java 进程当前的堆栈信息,为方便后续分析,将输出重定向到 jstack.txt 文件中。

1
jstack -l 1283 > jstack.txt

找出cpu占用率最高的java线程

要分析堆栈信息,需要先找到是哪个线程的 cpu 占用率比较高。再次使用 top 命令,加上 -H 参数开启线程模式,使用 -p 参数指定 java 进程的 ID。

1
top -H -p 1283

切换到高亮模式,按 cpu 排序,找出 cpu 占用率最高的 java 线程。

1
2
3
4
5
6
7
8
9
10
top - 23:36:11 up 28 min,  2 users,  load average: 1.00, 0.92, 0.63
Threads: 11 total, 1 running, 10 sleeping, 0 stopped, 0 zombie
%Cpu(s): 25.1 us, 0.2 sy, 0.0 ni, 74.6 id, 0.1 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 948304 total, 651280 free, 122268 used, 174756 buff/cache
KiB Swap: 102396 total, 102396 free, 0 used. 755080 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1292 gorden5+ 20 0 305312 13084 9056 R 99.9 1.4 6:08.71 java
1283 gorden5+ 20 0 305312 13084 9056 S 0.0 1.4 0:00.01 java
......

可以看到是 PID 为 1292 的 java 线程 cpu 占用率比较高。

定位问题代码

下面就可以分析通过 jstack 命令获取到的堆栈信息,在开始前需要对线程 ID 做下转换。因为堆栈信息里的线程 ID 使用的是十六进制,所以需要把 1292 转换为十六进制。

使用 printf 命令,加上 %x 参数

1
printf "0x%x\n" 1292

输出结果为:

1
0x50c

前面的 0x 及后面的 \n 只是为了使用方便。

打开 jstack.txt 文件,搜索 ID 为 0x50c 的线程,结果如下:

1
2
3
4
5
6
7
"busyThread" #7 prio=5 os_prio=0 tid=0x761b5000 nid=0x50c runnable [0x6485e000]
java.lang.Thread.State: RUNNABLE
at Top$Busy.run(Top.java:17)
at java.lang.Thread.run(Thread.java:745)

Locked ownable synchronizers:
- None

可见线程名为 busyThread ,执行的代码位置是 Top.java 的第 17 行,对应代码是 Busy 里的 i++