java应用的服务器cpu问题排查思路

cpu过高时,基本思路是:
查看cpu高的进程id->获得线程id->查看线程的执行栈 ;

一,进程

1
top

然后P(大写),会按照CPU排序

得到如下结果:
top
可以看到pid20469这个进程的cpu占比非常高

二,获得线程id

1
ps -mp 20469 -o THREAD,tid,time

tid
如图,可以看到tid20493这个线程的cpu占比达到了99.5

三,分析

3.1 工具环境

查看java的执行栈需要用到jstack,确保你的linux安装了jdk-devel套件

1
yum list --showduplicate | grep java-11 | grep devel

jdk-devel
根据输出结果进行安装

1
sudo yum install java-11-openjdk-devel.x86_64

3.2 翻译线程ID

1
printf "%x\n" 20493

输出500d

3.3 得到堆栈

1
jstack 20469 |grep 500d -A 60

传入pid和翻译的线程id500d得到堆栈结果:
jstack

四,测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main {
public static void main(String[] args) {
long pid = ProcessHandle.current().pid();
new Thread(() -> {
int i = 1;
while (i > 0) {
i++;
System.out.println(pid + " thread 1 output " + System.currentTimeMillis());
}
}).start();
new Thread(() -> {
while (true) {
System.out.println(pid + " thread 2 output " + System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}

这里启动了2个线程,都是死循环,但是第2个线程会sleep2秒。
死循环本身的cpu占用不高,占用高的原因是死循环里的System.out.println,这个方法会不断通过IO流输出数据。
将代码copy到服务器,新建Main.java,并直接通过执行

1
java Main.java

但是由于你通过ssh连接的服务器,System.out.println的结果也同时输出到终端上,这个结果是通过sshd服务回传到你的ssh窗口上,此时你在看top的进程时,也会看到sshd进程的cpu也很高,所以这里执行java的方法,最好通过nohup:

1
nohup java Main.java