Java应用CPU使用率高一般排查思路(centos) #
一、获取cpu高的Java进程 #
登陆问题服务器,执行如下命令:
top
可以看到类似图示的结果
- 图中可知,CPU高的Java进程 PID = 26132
二、获取Java进程中占cpu高的若干线程id #
根据步骤一的结果 PID = 26132 执行如下命令
top -H -p 26132
可以看到图示的结果
图中有1个线程CPU使用率最高78.7%,有8个线程cpu使用率次之,都为9%
三、找到线程对应的堆栈信息 #
取步骤二中CPU使用率最高的线程的PID(十进制)
执行如下命令
printf "%x\n" 26158
得到结果【662e】(PID的十六进制数)
此时我们已经知道了占用CPU最高的Java线程号为【662e】,然后我们需要将整个Java进程的瞬时线程堆栈dump下来,通过如下命令
jstack 26132 > threaddump.txt
然后在文件 threaddump.txt
中搜索【662e】(上面查到的占CPU高的线程),可以查到如下结果
可以看到,占CPU最高的线程为 “VM Thread”,VM Thread是JVM层面的一个线程,主要是负责对其他线程的创建,分配和对象的清理等工作的
从步骤二的图中,我们还可以看到,有其他8个线程,分别占了9%的使用率,取其中一个线程号进行相同操作
可以看到是名为 “Gang worker#0 (Parallel GC Threads)” 的线程,这个线程是JVM用于年轻代垃圾回收(minor gc)的线程
由此可以确认该Java进程存在GC问题,需要进行进一步的GC分析
四、根据堆栈信息确认下一步排查方向 #
通常,经过上面三步的排查,可以确认找到问题线程
按照经验,问题线程可以分成以下三种:
- 1、JVM线程
- 2、包含业务代码堆栈的线程
- 3、框架代码的线程(不包含业务代码堆栈的线程)
1、JVM线程 #
JIT线程 #
名称中含有 C1 CompilerThread
C2 CompilerThread
的线程为即时编译(JIT)线程,如果此类线程占用CPU较高,首先可以适当添加参数 -XX:CompileThreshold
的值,减少JIT频率,C2模式下该参数默认值是10000,C1模式下默认为1500。将JIT阈值设置为20000: -XX:CompileThreshold=20000
GC线程 #
名称中含有 Gang worker#0 (Parallel GC Threads)
G1 Main Concurrent Mark GC Thread
Gang worker#0 (G1 Parallel Marking Threads)
G1 Concurrent Refinement Thread
的线程为GC线程(此处以G1垃圾回收器举例,其他垃圾回收器线程名字有区别,详细可自行搜索)
GC线程CPU高的情况有单独的排查思路,移步 GC分析优化
2、包含业务代码堆栈的线程 #
如果问题线程堆栈中包含了业务代码(假设该线程是我们排查出来的CPU使用率高的线程),比如:
上述堆栈中包含了
at cn.com.duiba.duiba.test.web.controller.TestJvmController.threadTest(TestJvmController.java:37)
at cn.com.duiba.duiba.test.web.controller.TestJvmController.thread(TestJvmController.java:30)
明确指向了我们应用中的代码位置,这时就可以断定问题就是这个地方,进行代码修复即可
3、框架代码的线程(不包含业务代码堆栈的线程) #
这种情况一般比较麻烦,并且暂时没有比较通用的方法论对此问题进行覆盖
比如:问题堆栈指向lettuce框架内部的代码,这种情况一般需要先进行Google,看业界是否有其他人遇到类似的问题,如有,可以参考他们的解决方案。如没有,那么在确认自己使用方式没问题的情况下,在本地进行相关源码的debug,通过debug,对相关框架的源码进行熟悉,理解其原理,再通过其原理,推导出问题发生的原因