如何排查java应用中CPU使用率高或内存占用高的问题?这类问题的排查步骤基本通用的。如今经过一个具体的例子来讲明。java
最近有个线上项目天天0点事后CPU使用率会上升至200%到300%。数据库
本节内容是对排查过程的复盘,过程记录会比较详细。若是想知道具体的命令操做,能够直接看总结部分内容。数组
1)当CPU再次暴涨的时候,首先咱们能够经过top -c
查看CPU使用率高的进程的PID。
jvm
2)而后使用top -p PID -H
查看CPU使用率高的线程信息。若是CPU使用率高的线程是比较固定的,那么咱们记下对应线程的PID。 执行top -p 14639 -H
得出下图结果:
记下4个线程的PID: 1464三、1464四、1464一、14642jsp
3)接下来经过jstack PID > xxx.log
输出java应用当前堆栈信息到文件。工具
4)第2步中,咱们记下了CPU使用率高的线程PID,如今将4个线程的PID转成16进制: 393三、393四、393一、3932。接着在jstack输出的堆栈文件里,搜索nid等于393三、393四、393一、3932的线程信息。以下图:
从图中能够看出,对应的是GC线程。GC消耗大,那就有多是因为内存不足,频繁执行Full GC致使的。
再使用jstat -gc PID
查看jvm的GC状况,连续执行4次jstat -gc 14639
命令,发现FGC的数值变化比较快。这就说明Full GC确实执行很频繁。以下图:
网站
5)从第1步的截图中,能够看到CPU高的时候整个项目的内存占用1.3G左右。既然是内存问题,那么就须要使用jmap -histo:live PID > xxx.log
分析下jvm内存存活对象的统计状况。以下图:线程
从图中能够看出,byte对象([B)内存占用特别高,并且出现了一个具体的类:ByteArrayRow。这是一个jdbc作查询时候封装数据用的一个类,这个类里包含有byte数组。经过这个统计结果初步怀疑是作数据库查询时候,查询了太多内容到了内存,致使了内存不足。因为统计中没有出现具体的业务类,因此就觉得只是请求量比较大,致使的内存消耗过大。当时暂时将jvm的堆内存增大到2G。code
6)应用jvm堆内存调大以后,到了0点仍是出现了CPU高涨的问题。
内存占用了2G多,按照目前项目的请求量来讲,2G内存不可能被占满了,因此说明并非请求量大致使的结果,而是因为某块代码查询数据量过大致使的问题。orm
7)再次运行jmap -histo:live PID > xxx.log
将内存对象统计状况输出到文件。结果以下图:
此次的输出结果出现了业务类MiniProgram_User_Info,那就能够针对这个业务类去排查异常代码的位置了。不过,除非比较清楚这个类具体使用的地方,不然即便出现了具体的类名仍是比较难定位异常代码的位置。
这时候,咱们可使用jmap -dump:live,format=b,file=xxx.hprof PID
命令来输出内存对象的明细,来定位具体方法位置。这个命令是将内存里的全部信息都输出出来,输出的文件大小和内存大小基本一致。并且这个命令会致使应用暂时挂起,因此谨慎使用。
8)此次将内存明细输出以后,dump文件大小为2G。用jdk自带的jhat命令能够分析。以前分析其余dump文件用jhat仍是比较方便的。不过,分析此次的dump文件,给了10G运行内存给jhat命令才勉强打开了文件:jhat -J-mx10G -port 7170
。并且内存对象比较多,查找问题不方便。最后找到了一款神器: jprofiler。用jprofier分析dump文件须要的运行内存比较少,并且问题定位很方便。很快就定位出了内存中的大对象,占用了1G多内存的对象:
大对象对应的线程堆栈:
如上图,至此问题已经定位完成了。最后排查代码,最终发现凌晨时候,会将数据库里100多万条数据查询出来。内存不足致使频繁GC,结果就是CPU使用率暴涨。
1、在排查问题的过程当中针对CPU的问题,使用如下命令组合来排查问题
一、查看问题进程,获得进程PID:
top -c
二、查看进程里的线程明细,并手动记下CPU异常的线程PID:
top -p PID -H
三、使用jdk提供jstack命令打印出项目堆栈:
jstack pid > xxx.log
线程PID转成16进制,与堆栈中的nid对应,定位问题代码位置。
2、针对内存问题,使用如下命令组合来排查问题:
一、查看内存中的存活对象统计,找出业务相关的类名:
jmap -histo:live PID > xxx.log
二、经过简单的统计仍是无法定位问题的话,就输出内存明细来分析。这个命令会将内存里的全部信息都输出,输出的文件大小和内存大小基本一致。并且会致使应用暂时挂起,因此谨慎使用。
jmap -dump:live,format=b,file=xxx.hprof PID
三、 最后对dump出来的文件进行分析。文件大小不是很大的话,使用jdk自带的jhat命令便可:
jhat -J-mx2G -port 7170
四、dump文件太大的话,可使用jprofiler工具来分析。jprofiler工具的使用,这里不作详细介绍,有兴趣能够搜索一下。
3、须要分析GC状况,可使用如下命令:
jstat -gc PID
这里简单介绍一下java8里面这个命令得出的列表各个列的含义:
S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
通常会比较关注YGC和FGC的次数。
一、jstack输出的堆栈文件能够上传到下面这个网站,这个网站能够对堆栈内容进行统计汇总,方便咱们作分析:http://fastthread.io/index.jsp
二、排查过程小节中的第5步,jmap命令执行完后没有输出业务类,而第7步在却有。这个是由于第5步操做的时候只有1G多的内存,代码还没执行到业务对象的封装,内存就不够了,后续的代码没法被执行到。第7步操做的时候内存调整到2G,因此有部分业务对象已经被建立了。