一、场景描述 {#%E4%B8%80%E3%80%81%E5%9C%BA%E6%99%AF%E6%8F%8F%E8%BF%B0}
在实际应用场景中,JAVA 进程导致宿主机 CPU 使用率高的情况很常见,可能导致的原因很多:主机配置过低、代码质量低等;通常处于 bug 修复或者性能优化的需求,需要定位耗费大量 CPU 时间的罪魁祸首,这里提供几种常见的定位排查手段。
二、可选排查方式 {#%E4%BA%8C%E3%80%81%E5%8F%AF%E9%80%89%E6%8E%92%E6%9F%A5%E6%96%B9%E5%BC%8F}
【方式一】命令行 + jstack {#%E3%80%90%E6%96%B9%E5%BC%8F%E4%B8%80%E3%80%91%E5%91%BD%E4%BB%A4%E8%A1%8C-%2B-jstack}
jstack 是 Java 进程内的线程堆栈信息的常用工具,具体使用如下:
- 定位消耗 CPU 的进程 pid(按 P 排序):
top -c
- 可以将进程堆栈导出:
jstack -l <pid> > jstack.txt
,然后加载到本地客户端工具或者网页工具(如:heapdump)中进行分析 - 也可以完全使用命令行分析,定位消耗 CPU 的具体线程及其堆栈:
top -Hp <pid>
;输入 P 按 CPU 使用率排序,记住线程 pid 并转化为 16 进制:printf '%x\n' ,得到 16 进制的 nid-16;接着在 jstack 中找到对应堆栈信息:jstack -l <pid> | grep <nid-16> -C5 -color
或者jstack -l <pid> | grep <nid-16>
- 检查 JAVA 进程是否存在死锁:
jstack -l -F <pid>
【方式二】Arthas {#%E3%80%90%E6%96%B9%E5%BC%8F%E4%BA%8C%E3%80%91arthas}
Arthas 是 Alibaba 开源的一个 Java 诊断工具,方便进行问题的定位和诊断,可以在线排查问题,无需重启;动态跟踪 Java 代码;实时监控 JVM 状态。
- 下载 jar 包:
curl -O https://alibaba.github.io/arthas/arthas-boot.jar
- 使用和业务应用相同的用户运行调试命令(会进入专用控制台并自动识别 java 进程并提供序号列表供选择):
java -jar arthas-boot.jar
- 使用说明:
- 输入help命令查看支持的选项,dashboard命令用于整体展示进程所有线程、内存、GC 等情况,通过thread的-n 5选项找出最忙的前 5 个线程、-b 选项找出阻塞其他线程的线程,再输入thread 查看详情,使用trace或者tt命令进行跟踪分析,输入stop退出。
- profiler命令支持生成应用热点的火焰图,默认生成的是 cpu 的火焰图(即 event 为 cpu,可以用 --event 参数来指定,支持的 event 可以使用profiler list命令获取), 常规命令:
启动采样:profiler start
查看已获取的样本数:profiler getSamples
查看采样状态:profiler status
停止采样:profiler stop
(支持通过--file指定保存的文件名、--format html指定输出为 html 格式,默认为 svg)
- 火焰图分析说明:
y 轴表示调用栈,每一层都是一个函数。调用栈越深,火焰就越高,顶部就是正在执行的函数,下方都是它的父函数
x 轴表示抽样数,如果一个函数在 x 轴占据的宽度越宽,就表示它被抽到的次数多、即执行的时间长。注意,x 轴不代表时间,而是所有的调用栈合并后,按字母顺序排列的
颜色没有特殊含义,因为火焰图表示的是 CPU 的繁忙程度,所以一般选择暖色调
火焰图就是看顶层的哪个函数占据的宽度最大。只要有"平顶"(plateaus),就表示该函数可能存在性能问题
【方式三】show-busy-java-threads 脚本 {#%E3%80%90%E6%96%B9%E5%BC%8F%E4%B8%89%E3%80%91show-busy-java-threads-%E8%84%9A%E6%9C%AC}
下载安装包:wget --no-check-certificate https://github.com/oldratlee/useful-scripts/archive/release.zip
解压:unzip release.zip && cd useful-scripts-release
使用(找出指定 java 进程中最消耗 CPU 的线程(缺省 5 个),打印出其线程栈):./show-busy-java-threads -p <pid>