系统运行缓慢,CPU 100%,以及Full GC次数过多问题的排查思路

  • 时间:
  • 浏览:4

来源:http://t.cn/EI9JdBu


处理过线上问题图片的同学基本上后会 遇到系统老会 运行缓慢,CPU 1150%,以及Full GC次数太多的问题图片。当然,哪此问题图片的最终由于 的直观问题图片而是系统运行缓慢,而是有血块的报警。本文主要针对系统运行缓慢你这俩 问题图片,提供该问题图片的排查思路,从而定位出问题图片的代码点,进而提供处理该问题图片的思路。

对于线上系统老会 产生的运行缓慢问题图片,不可能 该问题图片由于 线上系统不可用,真难 首先还要做的而是,导出jstack和内存信息,而是重启系统,尽快保证系统的可用性。你这俩 情況不可能 的由于 主要有某种:

  • 代码中某个位置读取数据量较大,由于 系统内存耗尽,从而由于 Full GC次数太多,系统缓慢;
  • 代码涵盖比较耗CPU的操作,由于 CPU过低,系统运行缓慢;

相对来说,这是再次出现频率最高的某种线上问题图片,而是它们会直接由于 系统不可用。另外有几种情況也会由于 某个功能运行缓慢,而是不至于由于 系统不可用:

  • 代码某个位置有阻塞性的操作,由于 该功能调用整体比较耗时,但再次出现是比较随机的;
  • 某个进程不可能 某种由于 而进入WAITING情況,此时该功能整体不可用,而是无法复现;
  • 不可能 锁使用不当,由于 多个进程进入死锁情況,从而由于 系统整体比较缓慢。

对于这某种情況,通过查看CPU和系统内存情況是无法查看出具体问题图片的,不可能 它们相对来说都不 具有一定阻塞性操作,CPU和系统内存使用情況都不 高,而是功能却比较慢。下面亲戚亲戚大伙儿 就通过查看系统日志来一步一步甄别上述几种问题图片。

1. Full GC次数太多

相对来说,你这俩 情況是最容易再次出现的,尤其是新功能上线时。对于Full GC较多的情況,其主要有如下另另有多少 多内控 :

  • 线上多个进程的CPU都超过了1150%,通过jstack命令可不能否不能不可不能否都看哪此进程主而是垃圾回收进程
  • 通过jstat命令监控GC情況,可不能否不能不可不能否都看Full GC次数非常多,而是次数在不断增加。

首先亲戚亲戚大伙儿 可不能否不能不可不能否使用 top命令查看系统CPU的占用情況,如下是系统CPU较高的另另有多少 多示例:

top - 08:31:10 up 150 min,  0 users,  load average: 0.73, 0.58, 0.34KiB Mem:   20464150 total,  1923864 used,   122596 free,    14388 buffersKiB Swap:  1048572 total,        0 used,  1048572 free.  1192352 cached Mem

 PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND    9 root      20   0 25571150 288976  15812 S  98.0 14.1   0:42.150 java

可不能否不能不可不能否都看,有另另有多少 多Java进程此时CPU占用量达到了98.8%,此时亲戚亲戚大伙儿 可不能否不能不可不能否群克隆该进程id 9,而是使用如下命令查看呢该进程的各个进程运行情況:

top -Hp 9

该进程下的各个进程运行情況如下:

top - 08:31:16 up 150 min,  0 users,  load average: 0.75, 0.59, 0.35Threads:  11 total,   1 running,  10 sleeping,   0 stopped,   0 zombie%Cpu(s):  3.5 us,  0.6 sy,  0.0 ni, 95.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 stKiB Mem:   20464150 total,  1924856 used,   1211504 free,    14396 buffersKiB Swap:  1048572 total,        0 used,  1048572 free.  1192532 cached Mem

 PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND   10 root      20   0 25571150 289824  15872 R 79.3 14.2   0:41.49 java   11 root      20   0 25571150 289824  15872 S 13.2 14.2   0:06.78 java

可不能否不能不可不能否都看,在进程为 9的Java进程中各个进程的CPU占用情況,接下来亲戚亲戚大伙儿 可不能否不能不可不能否通过jstack命令查看进程id为 10的进程为哪此耗费CPU最高。还要注意的是,在jsatck命令展示的结果中,进程id都转去掉 了十六进制形式。可不能否不能不可不能否用如下命令查看转换结果,不可不能否不可不能否不可不能否找另另有多少 多科学计算器进行转换:

root@a39de7e7934b:/# printf "%x\n" 10a

这里打印结果说明该进程在jstack中的展现形式为 0xa,通过jstack命令亲戚亲戚大伙儿 可不能否不能不可不能否都看如下信息:

"main" #1 prio=5 os_prio=0 tid=0x00007f871115091150 nid=0xb runnable [0x00007f871fe411150]   java.lang.Thread.State: RUNNABLE    at com.aibaobei.chapter2.eg2.UserDemo.main(UserDemo.java:9)

"VM Thread" os_prio=0 tid=0x00007f8711506e000 nid=0xa runnable

这里的VM Thread一行的最后显示 nid=0xa,这里nid的意思而是操作系统进程id的意思。而VM Thread指的而是垃圾回收的进程。这里亲戚亲戚大伙儿 基本可不能否不能不可不能否不可不能否不可不能否确定,当前系统缓慢的由于 主而是垃圾回收过于频繁,由于 GC停顿时间较长。亲戚亲戚大伙儿 通过如下命令可不能否不能不可不能否查看GC的情況:

root@8d361241507a0:/# jstat -gcutil 9 11150 10  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT  0.00   0.00   0.00  75.07  59.09  59.150   3259    0.919  6517    7.715    8.635  0.00   0.00   0.00   0.08  59.09  59.150   31506    0.9150  6611    7.822    8.752  0.00   0.00   0.00   0.08  59.09  59.150   3351    0.943  6701    7.924    8.867  0.00   0.00   0.00   0.08  59.09  59.150   3397    0.955  6793    8.029    8.984

可不能否不能不可不能否都看,这里FGC指的是Full GC数量,这里高达6793,而是还在不断增长。从而进一步证实了是不可能 内存溢出由于 的系统缓慢。真难 这里确认了内存溢出,而是要怎样查看你是哪此对象由于 的内存溢出呢,你这俩 可不能否不能不可不能否dump出内存日志,而是通过eclipse的mat工具进行查看,如下是其展示的另另有多少 多对象树内控 :

经过mat工具分析刚刚,亲戚亲戚大伙儿 基本上就能确定内存中主而是哪个对象比较消耗内存,而是找到该对象的创建位置,进行处理即可。这里的主而是PrintStream最多,而是亲戚亲戚大伙儿 不可不能否不可不能否不可不能否都看,其内存消耗量不可不能否不可不能否12.2%。也而是说,其还过低以由于 血块的Full GC,此时亲戚亲戚大伙儿 还要考虑另外某种情況,而是代码不可能 第三方依赖的包涵盖显示的 System.gc()调用。你这俩 情況亲戚亲戚大伙儿 查看dump内存得到的文件即可判断,不可能 其会打印GC由于 :

[Full GC (System.gc()) [Tenured: 262546K->262546K(349568K), 0.0014879 secs] 262546K->262546K(1506816K), [Metaspace: 3109K->3109K(1056768K)], 0.0015151 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [DefNew: 2795K->0K(157248K), 0.00011504 secs][Tenured: 262546K->402K(349568K), 0.0012949 secs] 265342K->402K(1506816K), [Metaspace: 3109K->3109K(1056768K)], 0.0014699 secs] [Times: user=0.00

比如这里第一次GC是不可能 System.gc()的显示调用由于 的,而第二次GC则是JVM主动发起的。总结来说,对于Full GC次数太多,主要有以下某种由于 :

  • 代码中一次获取了血块的对象,由于 内存溢出,此时可不能否不能不可不能否通过eclipse的mat工具查看内存涵盖哪此对象比较多;
  • 内存占用不高,而是Full GC次数还是比较多,此时不可能 是显示的 System.gc()调用由于 GC次数太多,这可不能否不能不可不能否通过去掉  -XX:+DisableExplicitGC来禁用JVM对显示GC的响应。

2. CPU过低

在前面第某些中,亲戚亲戚大伙儿 讲到,CPU过低不可能 是系统频繁的进行Full GC,由于 系统缓慢。而亲戚亲戚大伙儿 平常而是可能 遇到比较耗时的计算,由于 CPU过低的情況,此时查看土措施虽然与上端的非常类似于于。首先亲戚亲戚大伙儿 通过 top命令查看当前CPU消耗过低的进程是哪个,从而得到进程id;而是通过 top-Hp<pid>来查看该进程涵盖哪此进程CPU过低,一般超过150%而是比较高的,150%左右是合理情況。刚刚亲戚亲戚大伙儿 就能得到CPU消耗比较高的进程id。接着通过该 进程id的十六进制表示jstack日志中查看当前进程具体的堆栈信息。

在这里亲戚亲戚大伙儿 就可不能否不能不可不能否区分由于 CPU过低的由于 具体是Full GC次数太多还是代码涵盖比较耗时的计算了。不可能 是Full GC次数太多,真难 通过 jstack得到的进程信息会是类似于于于VM Thread类似于于的进程,而不可能 是代码涵盖比较耗时的计算,真难 亲戚亲戚大伙儿 得到的而是另另有多少 多进程的具体堆栈信息。如下是另另有多少 多代码涵盖比较耗时的计算,由于 CPU过低的进程信息:

这可不能否不能不可不能否不可不能否不可不能否都看,在请求UserController的刚刚,不可能 该Controller进行了另另有多少 多比较耗时的调用,由于 该进程的CPU老会 指在1150%。亲戚亲戚大伙儿 可不能否不能不可不能否根据堆栈信息,直接定位到UserController的34行,查看代码中具体是哪此由于 由于 计算量真难 之高。

3. 不定期再次出现的接口耗时问题图片

对于你这俩 情況,比较典型的例子而是,亲戚亲戚大伙儿 某个接口访问老会 还要2~3s不可不能否返回。这是比较麻烦的某种情況,不可能 一般来说,其消耗的CPU太多,而是占用的内存而是高,也而是说,亲戚亲戚大伙儿 通过上述某种土措施进行排查是无法处理你这俩 问题图片的。而是不可能 刚刚的接口耗时比较大的问题图片是不定时再次出现的,这就由于 了亲戚亲戚大伙儿 在通过 jstack命令即使得到了进程访问的堆栈信息,亲戚亲戚大伙儿 也真难 判断具体哪个进程是正在执行比较耗时操作的进程。

对于不定时再次出现的接口耗时比较严重的问题图片,亲戚亲戚大伙儿 的定位思路基本如下:首先找到该接口,通过压测工具不断加大访问力度,不可能 说该接口涵盖某个位置是比较耗时的,不可能 亲戚亲戚大伙儿 的访问的频率非常高,真难 大多数的进程最终都将阻塞于该阻塞点,刚刚通太多个进程具有相同的堆栈日志,亲戚亲戚大伙儿 基本上就可不能否不能不可不能否定位到该接口中比较耗时的代码的位置。如下是另另有多少 多代码涵盖比较耗时的阻塞操作通过压测工具得到的进程堆栈日志:

"http-nio-150150-exec-2" #29 daemon prio=5 os_prio=31 tid=0x00007fd08cb215000 nid=0x91503 waiting on condition [0x00007000031d11500]   java.lang.Thread.State: TIMED_WAITING (sleeping)    at java.lang.Thread.sleep(Native Method)    at java.lang.Thread.sleep(Thread.java:340)    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)    at com.aibaobei.user.controller.UserController.detail(UserController.java:18)

"http-nio-150150-exec-3" #150 daemon prio=5 os_prio=31 tid=0x00007fd08cb27000 nid=0x6203 waiting on condition [0x00007000032d11500]   java.lang.Thread.State: TIMED_WAITING (sleeping)    at java.lang.Thread.sleep(Native Method)    at java.lang.Thread.sleep(Thread.java:340)    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)    at com.aibaobei.user.controller.UserController.detail(UserController.java:18)"http-nio-150150-exec-4" #31 daemon prio=5 os_prio=31 tid=0x00007fd08d0fa000 nid=0x6403 waiting on condition [0x00007000033db000]   java.lang.Thread.State: TIMED_WAITING (sleeping)    at java.lang.Thread.sleep(Native Method)    at java.lang.Thread.sleep(Thread.java:340)    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)    at com.aibaobei.user.controller.UserController.detail(UserController.java:18)

从上端的日志可不能否不能不可不能否看你出,这里有多个进程都阻塞在了UserController的第18行,说明这是另另有多少 多阻塞点,也而由于 该接口比较缓慢的由于 。

4. 某个进程进入WAITING情況

对于你这俩 情況,这是比较罕见的某种情況,而是也是有不可能 再次出现的,而是不可能 其具有一定的“不可复现性”,因而亲戚亲戚大伙儿 在排查的刚刚是非常难以发现的。笔者刚刚就遇到过类似于于的你这俩 情況,具体的场景是,在使用CountDownLatch时,不可能 还要每另另有多少 多并行的任务都执行完成刚刚才会唤醒主进程往下执行。而当时亲戚亲戚大伙儿 是通过CountDownLatch控制多个进程连接并导出用户的gmail邮箱数据,这其涵盖另另有多少 多进程连接上了用户邮箱,而是连接被服务器挂起了,由于 该进程老会 等待时间时间服务器的响应。最终由于 亲戚亲戚大伙儿 的主进程和其余多少进程都指在WAITING情況。

对于刚刚的问题图片,查都看jstack日志的读者应该都知道,正常情況下,线上大多数进程都不 指在 TIMED_WAITING情況,而亲戚亲戚大伙儿 这里出问题图片的进程指在的情況与其是一模一样的,这就非常容易混淆亲戚亲戚大伙儿 的判断。处理你这俩 问题图片的思路主要如下:

  • 通过grep在jstack日志中找出所有的指在 TIMED_WAITING情況的进程,将其导出到某个文件中,如a1.log,如下是另另有多少 多导出的日志文件示例:
"Attach Listener" #13 daemon prio=9 os_prio=31 tid=0x00007fe690061500 nid=0xd07 waiting on condition [0x0000000000000000]"DestroyJavaVM" #12 prio=5 os_prio=31 tid=0x00007fe6900615000 nid=0x21503 waiting on condition [0x0000000000000000]"Thread-0" #11 prio=5 os_prio=31 tid=0x00007fe6900611500 nid=0x5a03 waiting on condition [0x0000700003ad1500]"C1 CompilerThread3" #9 daemon prio=9 os_prio=31 tid=0x00007fe68c00a000 nid=0xa903 waiting on condition [0x0000000000000000]
  • 等待时间一段时间刚刚,比如10s,再次对jstack日志进行grep,将其导出到刚刚文件,如a2.log,结果如下所示:
"DestroyJavaVM" #12 prio=5 os_prio=31 tid=0x00007fe6900615000 nid=0x21503 waiting on condition [0x0000000000000000]"Thread-0" #11 prio=5 os_prio=31 tid=0x00007fe6900611500 nid=0x5a03 waiting on condition [0x0000700003ad1500]"VM Periodic Task Thread" os_prio=31 tid=0x00007fe68d111500 nid=0xa1503 waiting on condition
  • 重复步骤2,待导出3~另另有多少 多文件刚刚,亲戚亲戚大伙儿 对导出的文件进行对比,找出其中在这多少文件中老会 都指在的用户进程,你这俩 进程基本上就可不能否不能不可不能否确认是涵盖了指在等待时间情況有问题图片的进程。不可能 正常的请求进程是不想在20~150s刚刚还是指在等待时间情況的。
  • 经过排查得到哪此进程刚刚,亲戚亲戚大伙儿 可不能否不能不可不能否继续对其堆栈信息进行排查,不可能 该进程某种就应该指在等待时间情況,比如用户创建的进程池中指在空闲情況的进程,真难 你这俩 进程的堆栈信息中是不想涵盖用户自定义的类的。哪此都可不能否不能不可不能否排除掉,而剩下的进程基本上就可不能否不能不可不能否确认是亲戚亲戚大伙儿 要找的有问题图片的进程。通过其堆栈信息,亲戚亲戚大伙儿 就可不能否不能不可不能否得出具体是在哪个位置的代码由于 该进程指在等待时间情況了。

这里还要说明的是,亲戚亲戚大伙儿 在判断是有无为用户进程时,可不能否不能不可不能否通过进程最前面的进程名来判断,不可能 一般的框架的进程命名都不 非常规范的,亲戚亲戚大伙儿 通过进程名就可不能否不能不可不能否直接判断得出该进程是某些框架中的进程,你这俩 进程基本可不能否不能不可不能否不可不能否不可不能否排除掉。而剩余的,比如上端的 Thread-0,以及亲戚亲戚大伙儿 可不能否不能不可不能否辨别的自定义进程名,哪此都不 亲戚亲戚大伙儿 还要排查的对象。

经过上端的土措施进行排查刚刚,亲戚亲戚大伙儿 基本上就可不能否不能不可不能否得出这里的 Thread-0而是亲戚亲戚大伙儿 要找的进程,通过查看其堆栈信息,亲戚亲戚大伙儿 就可不能否不能不可不能否得到具体是在哪个位置由于 其指在等待时间情況了。如下示例中则是在SyncTask的第8行由于 该进程进入等待时间了。

"Thread-0" #11 prio=5 os_prio=31 tid=0x00007f9de08c7000 nid=0x51503 waiting on condition [0x0000700001f89000]   java.lang.Thread.State: WAITING (parking)    at sun.misc.Unsafe.park(Native Method)    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:1504)    at com.aibaobei.chapter2.eg4.SyncTask.lambda$main$0(SyncTask.java:8)    at com.aibaobei.chapter2.eg4.SyncTask$$Lambda$1/1791741888.run(Unknown Source)    at java.lang.Thread.run(Thread.java:748)

5. 死锁

对于死锁,你这俩 情況基本上很容易发现,不可能 jstack可不能否不能不可不能否帮助亲戚亲戚大伙儿 检查死锁,而是在日志中打印具体的死锁进程信息。如下是另另有多少 多产生死锁的另另有多少 多 jstack日志示例:

可不能否不能不可不能否都看,在jstack日志的底部,其直接帮亲戚亲戚大伙儿 分析了日志中指在哪此死锁,以及每个死锁的进程堆栈信息。这里亲戚亲戚大伙儿 有另另有多少 多用户进程分别等待时间时间对方释放锁,而被阻塞的位置都不 在ConnectTask的第5行,此时亲戚亲戚大伙儿 就可不能否不能不可不能否直接定位到该位置,而是进行代码分析,从而找到产生死锁的由于 。

6. 小结

本文主要讲解了线上不可能 再次出现的某种由于 系统缓慢的情況,详细分析了部分情況产生时的问题图片,不可能 根据问题图片亲戚亲戚大伙儿 可不能否不能不可不能否通过哪此土措施定位得到是你这俩 由于 由于 的系统缓慢。简要的说,亲戚亲戚大伙儿 进行线上日志分析时,主不可不能否不可不能否不可不能否分为如下步骤:

  • 通过 top命令查看CPU情況,不可能 CPU比较高,则通过 top-Hp<pid>命令查看当前进程的各个进程运行情況,找出CPU过低的进程刚刚,将其进程id转换为十六进制的表现形式,而是在jstack日志中查看该进程主要在进行的工作。这里又分为某种情況
    • 不可能 是正常的用户进程,则通过该进程的堆栈信息查看其具体是在哪处用户代码处运行比较消耗CPU;
    • 不可能 该进程是 VMThread,则通过 jstat-gcutil<pid><period><times>命令监控当前系统的GC情況,而是通过 jmapdump:format=b,file=<filepath><pid>导出系统当前的内存数据。导出刚刚将内存情況装进 eclipse的mat工具中进行分析即可得出内存中主而是哪此对象比较消耗内存,进而可不能否不能不可不能否处理相关代码;
  • 不可能 通过 top命令都看CPU从不高,而是系统内存占用率也比较低。此时就可不能否不能不可不能否考虑是有无是不可能 另外某种情況由于 的问题图片。具体的可不能否不能不可不能否根据具体情況分析:
    • 不可能 是接口调用比较耗时,而是是不定时再次出现,则可不能否不能不可不能否通过压测的土措施加大阻塞点再次出现的频率,从而通过 jstack查看堆 栈信息,找到阻塞点;
    • 不可能 是某个功能再次出现停滞的情況,你这俩 情況也无法复现,此时可不能否不能不可不能否通太多次导出 jstack日志的土措施对比哪此用户进程是老会 都指在等待时间情況,哪此进程而是不可能 过低图片的进程;
    • 不可能 通过 jstack可不能否不能不可不能否查都看死锁情況,则可不能否不能不可不能否检查产生死锁的另另有多少 多进程的具体阻塞点,从而处理相应的问题图片。

本文主而是提出了某种常见的由于 线上功能缓慢的问题图片,以及排查思路。当然,线上的问题图片再次出现的形式是多种多样的,而是一定局限于这几种情況,不可能 亲戚亲戚大伙儿 不想可不能否仔细分析哪此问题图片再次出现的场景,就可不能否不能不可不能否根据具体情況具体分析,从而处理相应的问题图片。

本文由

进程猿DD

发布在

ITPUB

,转载此文请保持文章详细性,并请附上文章来源(ITPUB)及本页链接。

原文链接:http://www.itpub.net/2019/06/04/20150/