使用阿里 Arthas 工具分析 CPU 飙高

Arthas是阿里开源的 JAVA 诊断工具,相比 JDK 内置的诊断工具,要更人性化,并且功能强大,可以实现许多问题的一键定位,而且可以一键反编译类查看源码 , 甚至是直接进行生产代码热修复,实现在一个工具内快速定位和修复问题的一站式服务 。
今天,我就带你使用 Arthas 定位一个 CPU 使用高的问题,系统学习下这个工具的使用 。
首先,下载并启动 Arthas:
curl -O https://alibaba.Github.io/arthas/arthas-boot.jarjava -jar arthas-boot.jar启动后,直接找到我们要排查的 JVM 进程,然后可以看到 Arthas 附加进程成功:
【使用阿里 Arthas 工具分析 CPU 飙高】[INFO] arthas-boot version: 3.1.7[INFO] Found existing java process, please choose one and hit RETURN.* [1]: 12707[2]: 30724 org.jetbrAIns.jps.cmdline.Launcher[3]: 30725 org.geekbang.time.commonmistakes.troubleshootingtools.highcpu.HighCPUApplication[4]: 24312 sun.tools.jconsole.JConsole[5]: 26328 org.jetbrains.jps.cmdline.Launcher[6]: 24106 org.NETbeans.lib.profiler.server.ProfilerServer3[INFO] arthas home: /Users/zhuye/.arthas/lib/3.1.7/arthas[INFO] Try to attach process 30725[INFO] Attach process 30725 success.[INFO] arthas-client connect 127.0.0.1 3658,---.,------. ,--------.,--.,--.,---.,---. /O|.--. ''--..--'|'--'| /O'.-'|.-.||'--'.'|||.--.||.-.|`.`-.|| |||||||||||| ||.-'|`--' `--'`--' '--'`--'`--'`--'`--' `--'`-----'wikihttps://alibaba.github.io/arthastutorials https://alibaba.github.io/arthas/arthas-tutorialsversion3.1.7pid30725time2020-01-30 15:48:33输出 help 命令,可以看到所有支持的命令列表 。今天,我们会用到 dashboard、thread、jad、watch、ognl 命令,来定位这个 HighCPUApplication 进程 。你可以通过官方文档:https://arthas.aliyun.com/doc/commands.html , 查看这些命令的完整介绍:

使用阿里 Arthas 工具分析 CPU 飙高

文章插图
图片
dashboard 命令用于整体展示进程所有线程、内存、GC 等情况,其输出如下:
使用阿里 Arthas 工具分析 CPU 飙高

文章插图
图片
可以看到,CPU 高并不是 GC 引起的,占用 CPU 较多的线程有 8 个,其中 7 个是 ForkJoinPool.commonPool 。
ForkJoinPool.commonPool 是并行流默认使用的线程池 。
所以,此次 CPU 高的问题,应该出现在某段并行流的代码上 。
接下来 , 要查看最繁忙的线程在执行的线程栈,可以使用 thread -n 命令 。这里,我们查看下最忙的 8 个线程:
thread -n 8输出如下:
使用阿里 Arthas 工具分析 CPU 飙高

文章插图
图片
可以看到 , 由于这些线程都在处理 MD5 的操作,所以占用了大量 CPU 资源 。我们希望分析出代码中哪些逻辑可能会执行这个操作 , 所以需要从方法栈上找出我们自己写的类,并重点关注 。
由于主线程也参与了 ForkJoinPool 的任务处理,因此我们可以通过主线程的栈看到需要重点关注 org.geekbang.time.commonmistakes.troubleshootingtools.highcpu.HighCPUApplication 类的 doTask 方法 。
接下来,使用 jad 命令直接对 HighCPUApplication 类反编译:
jad org.geekbang.time.commonmistakes.troubleshootingtools.highcpu.HighCPUApplication可以看到,调用路径是 main->task()->doTask() , 当 doTask 方法接收到的 int 参数等于某个常量的时候,会进行 1 万次的 MD5 操作,这就是耗费 CPU 的来源 。那么 , 这个魔法值到底是多少呢?
使用阿里 Arthas 工具分析 CPU 飙高

文章插图
图片
你可能想到了,通过 jad 命令继续查看 User 类即可 。这里因为是 Demo,所以我没有给出很复杂的逻辑 。在业务逻辑很复杂的代码中,判断逻辑不可能这么直白 , 我们可能还需要分析出 doTask 的“慢”会慢在什么入参上 。
这时,我们可以使用 watch 命令来观察方法入参 。如下命令,表示需要监控耗时超过 100 毫秒的 doTask 方法的入参,并且输出入参,展开 2 层入参参数:
watch org.geekbang.time.commonmistakes.troubleshootingtools.highcpu.HighCPUApplication doTask '{params}' '#cost>100' -x 2可以看到 , 所有耗时较久的 doTask 方法的入参都是 0 , 意味着 User.ADMN_ID 常量应该是 0 。
使用阿里 Arthas 工具分析 CPU 飙高

文章插图
图片
最后,我们使用 ognl 命令来运行一个表达式,直接查询 User 类的 ADMIN_ID 静态字段来验证是不是这样,得到的结果果然是 0:


推荐阅读