当应用系统出现卡顿或者应用程序响应速度非常慢的时候,我们需要登录到应用所在主机进行查看、分析问题原因。
常用来排查Java进程问题命令:
使用top命令查看主机资源总体使用情况,及相关Java进程占用主机资源情况;
使用vmstat 2 5命令查看主机CPU、内存使用率;
使用df -h命令查看主机文件系统使用率;
使用netstat -na命令收集主机网络连接情况;
使用pstree -p PID | wc -l查看占用CPU资源较高的进程内的线程数;
使用${JAVA_HOME}/bin/jstat -gcutil PID 1000 10查看占用CPU资源较高的进程GC情况;
使用${JAVA_HOME}/bin/jmap -dump:live,format=b,file=PID.hprof PID导出占用CPU资源较高的进程堆快照信息,用来统计当前JVM堆中存放了哪些对象信息;
使用${JAVA_HOME}/bin/jstack PID > stack_PID.log导出占用CPU资源较高的线程栈快照信息,来最终定位占用CPU过高的代码模块。
1、大量高并发密集型计算
2、高并发的I/O读写
3、堆内存无法有效回收,FULLGC频繁
4、程序死循环
5、程序逻辑请求堵塞
案例1:Java进程耗用CPU高,但未触发FULLGC的情况
1、查看进程使用CPU、MEM情况
使用命令:top
可以看到PID为4267,USER为dform的进程CPU使用率高达1464.2%(即消耗当前主机14.64个CPU),严重超高。使用命令cat/proc/cpuinfo | grep processor |wc –l 可查看当前主机逻辑CPU个数。

2、根据问题进程的PID查询该进程的线程使用CPU、MEM情况
使用命令:top-p 4267-H
红框标出的10几个线程耗用CPU都接近或者超过了100%(即该进程下有10几个线程耗用CPU都高达1个左右)。

3、导出问题进程的线程栈信息
使用命令:
连续导出多个线程栈(stack)快照文件,每次导出间隔5s-10s
${JAVA_HOME}/bin/jstack4267 > stack_4267_1.log
${JAVA_HOME}/bin/jstack4267 > stack_4267_2.log
${JAVA_HOME}/bin/jstack4267 > stack_4267_3.log
4、将问题线程的PID(十进制)转换成十六进制的nid
使用命令:我们选取如下两个线程为例
(1)线程PID4477,转换之后的十六进制nid为117d
printf "%x\n"4477

(2)线程PID4542,转换之后的十六进制nid为11be

5、根据nid在第3步中导出的线程栈(stack)快照文件中查找对应的线程
(1)根据nid=0x117d查找到正在运行的程序代码模块,红框标出是该线程最后正在做的事物(线程执行顺序要从下向上看),绿框标出是应用侧代码模块。

(2)同理,根据nid=0x11be查找到正在运行的程序代码模块

最后将上述分析结果和线程栈(stack)快照文件一并反馈给应用侧核查和处理。
案例2:Java进程耗用CPU高,而且频繁触发FULLGC的情况
导致Java进程频繁FULLGC的原因有:
(1)应用代码逻辑设计不合理,对象创建太多,而且对象使用完后未及时释放资源,或者已不再有用的对象相互之间存在引用,导致该对象无法进行回收;
(2)消费者消费速度慢(或停止消费),而生产者不断往队列中投递任务,导致队列中任务累积过多,任务对象占用堆内存太多而产生FULLGC;
(3)应用进程创建线程太多(每个线程需要分配线程栈内存),也可能导致内存溢出OOM,触发FULLGC;
注:分析FULLGC的问题,除了要分析案例1中的线程栈(stack)快照信息外,还要导出JVM堆(heap)进行分析。
1、使用命令:导出JVM堆(heap)快照信息
${JAVA_HOME}/bin/jmapdump:live,format=b,file=17074.hprof17074
注:堆(heap)快照文件一般都比较大,有几个G大小(基本和参数-Xmx设置大小一致)。
2、使用Java堆分析工具MAT(MemoryAnalyzer tool)分析堆内存中对象占用情况
通过对堆内存快照文件17074.hprof的分析,发现有以下一些对象占用内存较多:

结合日志信息分析,应用一次性经过代理节点从数据节点中获取大量数据到堆内存中,并同时几条请求同时进行,该代理节点出现JVM堆内数据占用较大导致内存溢出,FULLGC无法回收,此后一直频繁FullGC。另根据GC信息分析,大约7个小时就会有一次较多请求刷新导致内存占用过多的情况。
最后将分析结果反馈给应用侧核查和处理。




