如何用 Arthas 在线诊断 Java 生产环境问题?
Arthas 可通过 Java Agent 挂载到运行中的 JVM,在不重启、不改代码的前提下,按 attach、dashboard/thread、sc/sm/jad、watch 的流程定位 CPU 飙高、接口变慢、返回值异常和死锁,并需注
学完这篇,你能在不重启、不改代码的前提下,用 Arthas 定位 Java 线上服务的 CPU 飙高、接口变慢、返回值不对、死锁这几类常见故障。
现在很多团队都会在服务器上常备一个 Arthas 包,它通过 Java Agent 挂到正在运行的 JVM 上。前提有两个:目标进程是用 JDK 启动的(不是精简过的 JRE),并且执行 java -jar 的用户与该进程的用户一致,否则会 attach 失败。
第一步:把 Arthas 挂到目标进程上
这一步的目标是让 Arthas 连上你的业务 JVM,连上后会看到一个 [arthas@进程号]$ 的命令提示符。
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
执行之后它会列出当前机器上所有 Java 进程,输入序号回车即可。如果服务器不能连外网,先在有网机器上下载全量包再上传;如果 attach 卡住,加上 --telnet-port 9999 --http-port 9998 换端口重试。
注意:Arthas 4.x 需要 JDK 8 及以上,但attach 到 JDK 6/7 的老应用要用 Arthas 3.x 版本,版本对不上会直接报错退出。
第二步:先用 dashboard 和 thread 了解整体情况
这一步是为了在动任何命令之前,看清 CPU、内存、线程的整体状况,避免一上来就抓细节。
输入 dashboard 会出现一个 5 秒刷新的实时面板,看到线程列表、堆内存、GC 次数和几个最忙的线程。按 q 退出。
接着专看线程:
thread -n 3 # CPU 占用最高的 3 个线程及堆栈
thread -b # 检测死锁,有死锁会直接打印
thread --state BLOCKED # 列出所有阻塞线程
thread -n 3 输出的堆栈是排查 CPU 飙高的第一手证据,通常能直接看出是死循环、正则回溯还是某个序列化在耗 CPU。
注意:
thread -b只能发现synchronized造成的死锁,ReentrantLock之类的锁它查不出来,这种情况要用jstack配合看。
第三步:确认线上跑的是不是你以为的那版代码
这一步解决"我本地明明是好的"这类问题,确认类从哪个 jar 加载、方法签名是什么、实际字节码长什么样。
sc -d com.example.order.OrderService # 类信息:ClassLoader、来自哪个 jar
sm com.example.order.OrderService getOrder # 方法签名与参数类型
jad com.example.order.OrderService # 反编译出源码
sc -d 里的 classLoaderHash 后面会用到,jad 出来的代码就是线上真正在跑的代码,跟你 IDE 里那份对比一下,版本不一致、被热更新覆盖过的问题立刻现形。
第四步:用 watch 抓入参、返回值和异常
这一步用来回答"这个方法到底收到了什么、返回了什么、有没有抛异常",是排查业务逻辑问题最常用的手段。
watch com.example.order.OrderService getOrder \
'{params, returnObj, throwExp}' -x 3 -n 5
参数含义:-x 3 是展开层级(对象字段打深一层就够,别开太大),-n 5 是最多观测 5 次后自动结束,-e 是只在抛异常时打印。想看方法执行前的入参就加 -b,执行后加 -s。
注意:
watch不带-n会一直挂着打印,如果方法本身是高频调用,日志量会瞬间压垮磁盘。生产上务必给-n,并在排查完后stop退出。
第五步:用 trace 和 monitor 定位慢在哪
这一步解决"接口慢但不知道慢在哪一行"的问题。
trace com.example.order.OrderService getOrder -n 5 --skipJDKMethod true
monitor -c 5 com.example.order.OrderService getOrder
trace 会打印方法内部每个子调用的耗时,--skipJDKMethod true 可以过滤掉 JDK 内部方法,输出干净很多。monitor -c 5 每 5 秒输出一次该方法的调用次数、成功率和平均 RT,适合判断是偶发还是持续性劣化。
如果只想看是谁调用了这个方法,用 stack com.example.order.OrderService getOrder -n 3。
注意:
trace对每个子调用都做字节码增强,深度大的方法会有明显性能损耗。生产上先加-n,别在交易高峰期长时间挂着。
第六步:查配置、查 Bean、改日志级别
这一步不用重启就能确认配置值、捞到 Spring 容器里的 Bean、临时调日志级别。
vmtool --action getInstances --className org.springframework.context.ApplicationContext \
--express 'instances[0].getBean("orderService")'
ognl '@java.lang.System@getProperty("spring.profiles.active")'
logger --name com.example --level debug
logger 改完立即生效,排查完记得用 logger --name com.example --level info 改回去。
第七步:收尾
排查结束后执行 stop 退出当前会话,shutdown 关闭 Arthas 服务端;如果只是临时观察,stop 就够了,不会影响业务进程。
注意:
redefine热更新只能改方法体,改不了方法签名和字段,且编译用的 JDK 版本必须与目标进程一致,生产环境建议只在预发演练过再用。
小结
- 启动:
java -jar arthas-boot.jar选进程号,用户权限要和目标 JVM 一致。 - 看全局:
dashboard看面板,thread -n 3看 CPU 热点,thread -b查死锁。 - 验代码:
sc -d看类来源,jad反编译对比版本。 - 抓数据:
watch看参数/返回值/异常,务必带-n控次数。 - 找耗时:
trace定位慢子调用,monitor看整体 RT 与成功率。 - 查配置:
vmtool拿 Bean,ognl读系统属性,logger临时调级别。 - 用完
stop退出,热更新和 trace 都要先在预发验证。
转载请注明出处,版权归原作者所有。