背景介绍
对于线上系统突然产生的运行缓慢问题,如果该问题导致线上系统不可用,那么首先需要做的就是,导出 jstack 和内存信息,然后重启系统,尽快保证系统的可用性。
这种情况可能的原因主要有两种:
- 代码中某个位置读取数据量较大,导致系统内存耗尽,从而导致 Full GC 次数过多,系统缓慢。
- 代码中有比较耗 CPU 的操作,导致 CPU 过高,系统运行缓慢。
相对来说,这是出现频率最高的两种线上问题,而且它们会直接导致系统不可用。
另外有几种情况也会导致某个功能运行缓慢,但是不至于导致系统不可用:
- 代码某个位置有阻塞性的操作,导致该功能调用整体比较耗时,但出现是比较随机的。
- 某个线程由于某种原因而进入 WAITING 状态,此时该功能整体不可用,但是无法复现。
- 由于锁使用不当,导致多个线程进入死锁状态,从而导致系统整体比较缓慢。
具体情况及定位步骤
简要的说,我们进行线上日志分析时,主要可以分为如下情况:
1、 CPU 比较高
-
通过 top 命令查看 CPU 情况,如果 CPU 比较高,则通过 top -Hp 命令查看当前进程的各个线程运行情况。
-
找出 CPU 过高的线程之后,将其线程 id 转换为十六进制的表现形式(printf “%x” ),然后在 jstack 日志中查看该线程主要在进行的工作(jstack 进程pid | grep 十六进制的线程tid 如:jstack 1956 | grep -A10 7b9:显示含有线程号为【7b9】的那一行以及其后10行的数据)。
这里又分为两种情况:
- 如果是正常的用户线程,则通过该线程的堆栈信息查看其具体是在哪处用户代码处运行比较消耗 CPU。(jstack -l pid(进程pid) > 文件路径名)
- 如果该线程是 VM Thread,则通过 jstat -gcutil 命令监控当前系统的 GC 状况。
然后通过 jmap dump:format=b,file= 导出系统当前的内存数据。导出之后将内存情况放到 Eclipse 的 Mat 工具中进行分析即可得出内存中主要是什么对象比较消耗内存,进而可以处理相关代码。
2、 CPU 并不高
- 如果通过 top 命令看到 CPU 并不高,并且系统内存占用率也比较低。此时就可以考虑是否是由于另外三种情况导致的问题。
具体的可以根据具体情况分析:
- 如果是接口调用比较耗时,并且是不定时出现,则可以通过压测的方式加大阻塞点出现的频率,从而通过 jstack 查看堆栈信息,找到阻塞点。
- 如果是某个功能突然出现停滞的状况,这种情况也无法复现,此时可以通过多次导出 jstack 日志的方式对比哪些用户线程是一直都处于等待状态,这些线程就是可能存在问题的线程。
- 如果通过 jstack 可以查看到死锁状态,则可以检查产生死锁的两个线程的具体阻塞点,从而处理相应的问题。
本文主要总结了五种常见的导致线上功能缓慢的问题,以及排查思路。当然,线上的问题出现的形式是多种多样的,也不一定局限于这几种情况。
参考
https://blog.csdn.net/keketrtr/article/details/54289134
https://blog.csdn.net/jerry024/article/details/8507589