背景
最近,有运维同事收到告警,提示服务器出现CPU占用100%的情况出现,并且严重影响服务性能,甚至导致一些功能不可用。接到上述情况反馈后,随即展开对问题的排查。
排查
CPU占用100%排查
-
定位进程:使用 top 命令,定位占用CPU最多的进程ID(例:7788)
-
定位线程:使用 top -Hp PID 命令,定位占用CPU的线程(例:top -Hp 7788;假设结果: 7789)
-
进制转换:
:在Jvm中,线程ID,是以16进制存储,所以需要将10进制线程ID转为10进制;
-
使用命令:printf "%x\n" 7789 ;得到16进制:1e6d
-
使用浏览器在线工具转换:得到16进制:1e6d
-
-
查询堆栈:使用 jstack 进程PID | grep 线程16进制 命令(jstack 7788 | grep 1e6d),查询处具体线程的堆栈信息;
-
定位线程:根据线程0x1e6d的堆栈信息,分析可能得原因;(本案例中,根据得到的堆栈信息如、判断JVM主要由于大量GC,而导致CPU占用过高)
堆栈信息:
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007fb22c01f000 nid=0x1e6d runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007fb22c021000 nid=0x1e6e runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007fb22c023000 nid=0x1e6f runnable
OOM问题排查
如上所述,根据线程堆栈,只是定位了,是由于大量GC导致的CPU过高,还需要进一步定位,大量GC的情况,初步怀疑是内存溢出问题;
当我们在Tomcat启动时,添加上如下JVM参数,那么发生OOM时,将自动输出Dump日志信息(其中第二个参数,是Dump文件的输出位置);
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=D:/logs/heapdump.hprof
-
配置JVM参数:如果JVM没有添加上述参数,那么建议加上;我们也可以使用
jmap -dump:format=b,file=heapdump.hprof <pid>
工具手动进行堆dump和线程dump -
获得Dump文件:如果发生OOM问题,我们可以在配置的目录,获取Dump文件(hprof文件)
-
分析Dump文件:
:得到Dump文件后,我们可以对其进行分析,以下提供两种方式:
-
jvisualvm:Jdk自带的分析工具,提供dump堆快照的功能,导入后点击一下即可;
-
mat:MAT(Memory Analyzer tool)是一款内存分析器工具,官方网站是:
https://www.eclipse.org/mat/
-
-
确定OOM代码位置:通过MAT分析工具,我们可以较方便的定位OOM代码位置;(在本案例中,原因是:一个查询返回List的方法,由于错误的表关联方式,导致返回几百万条记录,也没有做分页处理,最终暴露了OOM异常)
-
代码修复:在第4步中,确定了问题之后,我们就针对性的进行修复就好;
总结
本文基于一次典型的CPU打满问题,逐步分析,最终定位到发生OOM的代码行,并处理修复。在后续遇到类似问题时,可以参考此排查思路,进行处理。
同时,根据此次爆发OOM的原因,也可以发现,我们在查询列表数据时,特别是没有分页时,尤其要注意,返回的数据体量,避免返回数据过多,导致OOM问题。