现象:CPU跑满、内存快速增长、服务崩溃。
// 找到CPU高的进程pid
# top
// 导出dump文件
# jstack -l <pid> > cpu.log
// 找出cpu高的线程tid
ps -mp <pid> -o THREAD,tid,time | sort -rn
// 转换线程tid
printf "%x\n" <tid>
// 在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
//查询内存垃圾回收
jstat -gcutil pid 2000 10
jmap -histo <pid>
jmap -dump:format=b,file=dumpFileName <pid> //所有的
jmap -dump:format=live,file=dumpFileName <pid> //活着的
一般是GC问题、内存不足或内存泄漏
使用MAT工具进行内存泄露分析
http://www.eclipse.org/mat/downloads.php
//-XX:+HeapDumpOnOutOfMemoryError将内存溢出时堆信息导出
//-XX:HeapDumpPath=d:/a.dump指定堆信息导出的路径
-Xmx20m -Xms20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/a.dump -XX:+PrintGCDetails
- 问题: Locked ownable synchronizers