现象:CPU跑满、内存快速增长、服务崩溃。

  1. // 找到CPU高的进程pid
  2. # top
  3. // 导出dump文件
  4. # jstack -l <pid> > cpu.log
  5. // 找出cpu高的线程tid
  6. ps -mp <pid> -o THREAD,tid,time | sort -rn
  7. // 转换线程tid
  8. printf "%x\n" <tid>
  9. // 在cpu.log中查找转换后的tid

问题:C2 CompilerThread

原因和解释

定位到 C2 CompilerThread0这个线程占用了比较高的CPU。
C2 Compiler 是JVM在server模式下字节码编译器,JVM启动的时候所有代码都处于解释执行模式,当某些代码被执行到一定阈值次数,这些代码(称为热点代码)就会被 C2 Compiler编译成机器码,编译成机器码后执行效率会得到大幅提升。
流量进来后,大部分代码成为热点代码,这个过程中C2 Compiler需要频繁占用CPU来运行,当大部分热点代码被编译成机器代码后,C2 Compiler就不再长期占用CPU了,这个过程也可以看作抖动。

解决方案

(1)最直接有效的方法是“预热(warm up)”:可以使用Jmeter等压测工具模拟线上访问流量,让C2 Compiler预先将热点代码编译成机器码, 减少对正式环境流量的影响。

(2) 设置JVM启动参数:-XX:CICompilerCount=threads
编译线程数,默认是2, 可以设置4或6。
在默认值下抖动时CPU已经满载,设置成更多的线程也不一定起作用,但对于CPU“高而不满”的情况会有用,能减少抖动时间。

(3) 排查接口调用次数
spring boot actuator
http://host:port/metrics
排查调用次数(counter)和时间(gauge)

参考:
http://www.xiuson.com/?p=203
https://cyberdak.github.io/jvm/2017/03/25/jvm-restart-cause-high-load
http://www.blogjava.net/xylz/archive/2012/03/15/371966.html

问题:GC task thread

  1. //查询内存垃圾回收
  2. jstat -gcutil pid 2000 10
  3. jmap -histo
  4. jmap -dump:format=b,file=dumpFileName //所有的
  5. jmap -dump:format=live,file=dumpFileName //活着的

一般是GC问题、内存不足或内存泄漏

使用MAT工具进行内存泄露分析
http://www.eclipse.org/mat/downloads.php

  1. //-XX:+HeapDumpOnOutOfMemoryError将内存溢出时堆信息导出
  2. //-XX:HeapDumpPath=d:/a.dump指定堆信息导出的路径
  3. -Xmx20m -Xms20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/a.dump -XX:+PrintGCDetails