Arthas 入门指南:线上 Java 应用诊断利器 1. 背景通常本地开发环境无法访问生产环境。如果在生产环境中遇到问题则无法使用 IDE 远程调试。更糟糕的是在生产环境中调试是不可接受的因为它会暂停所有线程导致服务暂停。开发人员可以尝试在测试环境或者预发环境中复现生产环境中的问题。但是某些问题无法在不同的环境中轻松复现甚至在重新启动后就消失了。如果您正在考虑在代码中添加一些日志以帮助解决问题您将必须经历以下阶段测试、预发然后生产。这种方法效率低下更糟糕的是该问题可能无法解决因为一旦 JVM 重新启动它可能无法复现如上文所述。Arthas 旨在解决这些问题。开发人员可以在线解决生产问题。无需 JVM 重启无需代码更改。Arthas 作为观察者永远不会暂停正在运行的线程。2. Arthas 能为你做什么Arthas 是 Alibaba 开源的 Java 诊断工具深受开发者喜爱。当你遇到以下类似问题而束手无策时Arthas 可以帮助你解决这个类从哪个 jar 包加载的为什么会报各种类相关的 Exception我改的代码为什么没有执行到难道是我没 commit分支搞错了遇到问题无法在线上 debug难道只能通过加日志再重新发布吗线上遇到某个用户的数据处理有问题但线上同样无法 debug线下无法重现是否有一个全局视角来查看系统的运行状况有什么办法可以监控到 JVM 的实时运行状态怎么快速定位应用的热点生成火焰图怎样直接从 JVM 内查找某个类的实例Arthas 4.x 支持 JDK 8包括 JDK 17、JDK 21 和 JDK 25支持 Linux/Mac/Windows采用命令行交互模式同时提供丰富的 Tab 自动补全功能进一步方便进行问题的定位和诊断。3. 快速入门可以通过下面的方式自己动手实践快速入门。3.1 启动 math-gamecurl -O https://arthas.aliyun.com/math-game.jar java -jar math-game.jarmath-game 是一个简单的程序每隔一秒生成一个随机数再执行质因数分解并打印出分解结果。3.2 启动 arthas在命令行下面执行使用和目标进程一致的用户启动否则可能 attach 失败curl -O https://arthas.aliyun.com/arthas-boot.jar java -jar arthas-boot.jar执行该程序的用户需要和目标进程具有相同的权限。比如以 admin 用户来执行sudo su admin java -jar arthas-boot.jar或sudo -u admin -EH java -jar arthas-boot.jar。如果 attach 不上目标进程可以查看~/logs/arthas/目录下的日志。如果下载速度比较慢可以使用 aliyun 的镜像java -jar arthas-boot.jar --repo-mirror aliyun。java -jar arthas-boot.jar -h打印更多参数信息。选择应用 java 进程$ java -jar arthas-boot.jar * [1]: 35542 [2]: 71560 math-game.jarmath-game 进程是第 2 个则输入 2再输入回车/enter。Arthas 会 attach 到目标进程上并输出日志[INFO] Try to attach process 71560 [INFO] Attach process 71560 success. [INFO] arthas-client connect 127.0.0.1 3658 ,---. ,------. ,--------.,--. ,--. ,---. ,---. / O \ | .--. --. .--| -- | / O \ .- | .-. || --. | | | .--. || .-. |. -. | | | || |\ \ | | | | | || | | |.- | -- ---- -- -- -- ---- ------- wiki: https://arthas.aliyun.com/doc version: 3.0.5.20181127201536 pid: 71560 time: 2018-11-28 19:16:24 $3.3 查看 dashboard输入dashboard按回车/enter会展示当前进程的信息按 ctrlc 可以中断执行。$ dashboard ID NAME GROUP PRIORI STATE %CPU TIME INTERRU DAEMON 17 pool-2-thread-1 system 5 WAITIN 67 0:0 false false 27 Timer-for-arthas-dashb system 10 RUNNAB 32 0:0 false true 11 AsyncAppender-Worker-a system 9 WAITIN 0 0:0 false true 9 Attach Listener system 9 RUNNAB 0 0:0 false true 3 Finalizer system 8 WAITIN 0 0:0 false true 2 Reference Handler system 10 WAITIN 0 0:0 false true 4 Signal Dispatcher system 9 RUNNAB 0 0:0 false true 26 as-command-execute-dae system 10 TIMED_ 0 0:0 false true 13 job-timeout system 9 TIMED_ 0 0:0 false true 1 main main 5 TIMED_ 0 0:0 false false 14 nioEventLoopGroup-2-1 system 10 RUNNAB 0 0:0 false false 18 nioEventLoopGroup-2-2 system 10 RUNNAB 0 0:0 false false 23 nioEventLoopGroup-2-3 system 10 RUNNAB 0 0:0 false false 15 nioEventLoopGroup-3-1 system 10 RUNNAB 0 0:0 false false Memory used total max usage GC heap 32M 155M 1820M 1.77% gc.ps_scavenge.count 4 ps_eden_space 14M 65M 672M 2.21% gc.ps_scavenge.time(m 166 ps_survivor_space 4M 5M 5M s) ps_old_gen 12M 85M 1365M 0.91% gc.ps_marksweep.count 0 nonheap 20M 23M -1 gc.ps_marksweep.time( 0 code_cache 3M 5M 240M 1.32% ms) Runtime os.name Mac OS X os.version 10.13.4 java.version 1.8.0_162 java.home /Library/Java/JavaVir tualMachines/jdk1.8.0 _162.jdk/Contents/Hom e/jre3.4 通过 thread 命令获取 Main Classthread 1会打印线程 ID 1 的栈通常是 main 函数的线程。$ thread 1 | grep main( at demo.MathGame.main(MathGame.java:17)3.5 通过 jad 反编译 Main Class$ jad demo.MathGame ClassLoader: -sun.misc.Launcher$AppClassLoader3d4eac69 -sun.misc.Launcher$ExtClassLoader66350f69 Location: /tmp/math-game.jar /* Decompiled with CFR 0_132. */ package demo; import java.io.PrintStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Random; import java.util.concurrent.TimeUnit; public class MathGame { private static Random random new Random(); private int illegalArgumentCount 0; public/code/pre参考快速入门 | arthas