JavaEE应用CPU100%定位思路分析

17 阅读2分钟

定位CPU 100%的问题,可以从业务和技术两个角度分析。从业务上说,如果在执行某个已知的计算型业务操作后出现CPU飙升,就直接从这个操作入手,分析可能的直接或间接原因;在技术角度可以按一般思路展开分析,下面以一个实例说明。

如图1,测试的同事在性能测试时,发现微服务出现CPU持续100%的问题。该应用是部署在Docker容器中的Java微服务。

image.png

图1 CPU持续100%

(1)进入docker
docker ps | grep SERVICE_NAME   # SERVICE_NAME是微服务名称,如optimizer

docker exec -it CONTAINER_ID bash  # CONTAINER_ID是容器

(2)找到CPU高的线程

top -H  # -H是线程模式,对于Java应用来说可以看具体线程

结果如图2,可以看到,CPU比较高的线程ID是40506和51244

image.png

图2 top –H执行结果

(3)转为16进制
printf "%x\n" 51244  # \n是为了换行显示,可以不要

得到16进制数:c82c

(4)获取线程堆栈
如果服务中部署了jdk工具jstack,可以使用jstack 进行线程转储,但是一般现网环境不会部署jdk工具的(当然可以再上传,然后用相同用户执行),这里介绍使用Linux的kill命令打印线程转储信息。

kill的信号大概有60多种,最常用的是kill -9,它发送的是SIGKILL信号,表示强制终止线程,我们这里使用kill -3命令,它发送的是SIGQUIT信号,Java会转储指定的线程堆栈,但这并不会杀死进程。

部署在Tomcat中的服务一般会将日志输出到catalina.out(tomcat-catalina.log)等日志文件中。

如果需要取多次时间间隔的线程堆栈,写个循环就好了。
(5)日志过滤

cat tomcat-catalina.log | grep –A 50 c82c  # c82c是上面的pid=51244的线程

结果如图3所示,根据多个线程堆栈,可以看到这个线程一直处于RUNNABLE状态,这个例子根据线程名称、堆栈信息,已经可以大致清楚问题原因了(这里是解决NP-C问题的算法迭代产生的问题),如果还需要进一步分析,可以获取堆转储、使用MAT、JProfile等工具,这里就不展开了。

image.png

图3 c82c线程堆栈