测试代码:
```
package jdktest;
public class HoldCPUMain { //该车型简单占用cpu,4个用户线程,一个占用大量cpu资源,3个线程处于空闲状态
public static class HoldCPUTask implements Runnable {
@Override
public void run() {
while (true) {
double a = Math.random() * Math.random(); //大量占用cpu
}
}
}
public static class LazyTask implements Runnable {
@Override
public void run() {
try {
while(true) {
Thread.sleep(1000); //空闲线程
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new Thread(new HoldCPUTask()).start(); //开启线程,占用cpu
new Thread(new LazyTask()).start(); //3个空闲线程
new Thread(new LazyTask()).start();
new Thread(new LazyTask()).start();
}
}
```
某服务器上部署了若干tomcat实例,即若干垂直切分的Java站点服务,以及若干Java微服务,突然收到运维的CPU异常告警。
问:如何定位是哪个服务进程导致CPU过载,哪个线程导致CPU过载,哪段代码导致CPU过载?
步骤一、找到最耗CPU的进程PID
工具:top(可以配合Jps命令输出:13375 HoldCPUMain)
方法:
· 执行top -c ,显示进程运行信息列表
· 键入P (大写p),进程按照CPU使用率排序(大写M按内存排序)
·
输出:13375 root 20 0 2464304 21772 11488 S 98.7 1.2 6:31.28 java jdktest.HoldCPUMain
步骤二:找到该进程内最耗CPU的线程PID(假设步骤一得到进程pid是13375)
工具:top
方法:
· top -Hp 13375 ,显示一个进程的线程运行信息列表
· 键入P (大写p),线程按照CPU使用率排序
输出:13386 root 20 0 2464304 21772 11488 R 99.0 1.2 7:20.99 java
也可以安装sysstat,yum install -y sysstat,然后使用pidstat工具监控进程及线程的性能情况:
pidstat -p 13375 1 3 -u -t,其中-p指定进程pid,参数1 3表示每秒采样1次,合计采样3次,-u表示对cpu使用率的监控,监视进程信息,加上-t进一步监视线程信息
```
Average: UID TGID TID %usr %system %guest %CPU CPU Command
Average: 0 13375 - 98.01 0.33 0.00 98.34 - java
Average: 0 - 13375 0.00 0.00 0.00 0.00 - |__java
Average: 0 - 13376 0.00 0.00 0.00 0.00 - |__java
Average: 0 - 13377 0.00 0.00 0.00 0.00 - |__java
Average: 0 - 13378 0.00 0.00 0.00 0.00 - |__java
Average: 0 - 13380 0.00 0.00 0.00 0.00 - |__java
Average: 0 - 13381 0.00 0.00 0.00 0.00 - |__java
Average: 0 - 13382 0.00 0.00 0.00 0.00 - |__java
Average: 0 - 13383 0.00 0.00 0.00 0.00 - |__java
Average: 0 - 13384 0.00 0.00 0.00 0.00 - |__java
Average: 0 - 13385 0.00 0.00 0.00 0.00 - |__java
Average: 0 - 13386 98.01 0.00 0.00 98.01 - |__java
Average: 0 - 13387 0.00 0.00 0.00 0.00 - |__java
Average: 0 - 13388 0.00 0.00 0.00 0.00 - |__java
Average: 0 - 13389 0.00 0.00 0.00 0.00 - |__java
Average: 0 - 15131 0.00 0.00 0.00 0.00 - |__java
```
步骤三:将线程PID转化为16进制(假设步骤二得到线程pid是13386)
工具:printf
方法:printf %x\n 13386
这一步可以用计算器。之所以要转化为16进制,是因为堆栈里,线程id是用16进制表示的。
步骤四:查看堆栈,找到线程在干嘛(步骤三输出的线程pid的16进制是344a,a是小写)
工具:pstack/jstack/grep
方法:jstack 13375 | grep 0x344a -C5 --color
· 打印进程堆栈
· 通过线程id,过滤得到线程堆栈
找到了耗CPU高的线程对应的线程名称“Thread-0”,以及看到了该线程正在执行代码的堆栈。
```
"Thread-0" #8 prio=5 os_prio=0 tid=0x00007fa86c0c9800 nid=0x344a runnable [0x00007fa8497d1000]
java.lang.Thread.State: RUNNABLE
at jdktest.HoldCPUMain$HoldCPUTask.run(HoldCPUMain.java:9)
at java.lang.Thread.run(Thread.java:748)
```
或使用jstack -l 13375 > /tmp/t.txt输出到文件