Arthas在线诊断实战:trace、watch、jad、thread、dashboard

对应简历段落

简历中和 Arthas 相关的经历可以写成:

熟练使用 Arthas 对生产 Java 服务进行在线诊断,结合 dashboard、thread、trace、watch、jad、heapdump 等命令定位接口耗时、线程阻塞、参数异常、代码版本不一致和内存问题,支撑核心保险业务系统线上故障快速恢复。

面试官看到这段会非常容易追问细节:tracewatch 有什么区别?线上执行 watch 会不会有风险?jad 反编译解决过什么问题?thread -n 5 看到 CPU 高线程后怎么继续分析?dashboard 能看出哪些 JVM 风险?Arthas 是怎么 attach 到进程的?你有没有因为表达式写得太重影响线上?

这篇文章不把 Arthas 当命令手册,而是放到保险业务系统的线上诊断场景里讲。重点是:什么时候用哪个命令、看到结果后如何判断、如何控制风险、如何把观察结果转成修复方案。

业务背景

保险系统的线上问题经常发生在“不能重启、不能加日志、不能马上发版”的时候。比如承保接口突然变慢,但日志只打印了入口和异常,没有打印中间耗时;理赔查询返回数据为空,但测试环境无法复现;大 Excel 导出偶发 OOM,但不知道一次到底查了多少数据;某个定时任务凌晨跑满 CPU,但代码路径很深;新版本发布后怀疑某个实例没有更新到正确代码。

传统方式通常是补日志、发版、等复现。但生产问题等不起,尤其是核心交易链路,一次发版也有流程成本和风险。Arthas 的价值就在于在线观察 JVM 运行状态、方法调用、参数返回、线程栈和实际加载的字节码,不重启服务也能补齐很多证据。

一个典型场景是保单导出接口变慢。业务方说“导出卡住”,数据库同事说慢 SQL 不明显,应用日志只看到接口总耗时 60 秒。此时可以用 trace 看耗时到底在查询、对象转换、字典补全、Excel 写出还是文件上传;用 watch 看入参和返回值大小;用 thread 看是否有线程阻塞;用 dashboard 看 GC 和线程总体状态。如果怀疑线上代码与仓库不一致,可以用 jad 反编译实际运行的类。

核心原理

Arthas 通过 Java Attach 机制连接到目标 JVM,并利用字节码增强技术在运行时观察方法调用。它不是简单读日志,而是在目标方法执行前后插入观测逻辑,所以功能强大,也必须控制使用范围。

dashboard 是全局体检命令,可以快速看到线程、内存、GC、运行时间、系统负载等信息。它适合故障初期判断应用是否处于 GC 压力、线程暴涨、CPU 高或内存接近上限。

thread 用于看线程状态和线程栈。thread -n 5 可以查看 CPU 占用最高的线程,适合 CPU 飙高场景;thread <id> 可以查看指定线程栈;thread -b 可以查找阻塞其他线程的锁持有者。线程栈能告诉我们业务线程卡在数据库、Redis、锁竞争、文件 IO、正则、JSON 序列化还是死循环。

trace 用于跟踪方法内部调用链路耗时。它能显示一个入口方法下各子方法耗时占比,适合定位“这个接口慢在哪里”。但它默认只跟踪一层或有限路径,复杂调用链要逐步下钻,不宜一上来对高频方法大范围 trace。

watch 用于观察方法入参、返回值、异常和执行耗时。它适合回答“这个方法收到什么参数、返回了什么、抛了什么异常、结果集合多大”。表达式基于 OGNL,非常灵活,也有风险。如果打印巨大对象,可能给线上制造额外压力。

jad 用于反编译 JVM 中实际加载的类。它适合确认线上代码版本、查看动态代理增强后的逻辑、核对热修复或灰度实例是否加载了预期代码。很多时候仓库代码和线上字节码不完全一致,jad 能直接看 JVM 里的真相。

这几个命令组合起来,可以覆盖线上诊断的主要问题:全局状态看 dashboard,线程问题看 thread,慢方法看 trace,参数返回看 watch,代码一致性看 jad

项目落地

项目里使用 Arthas,首先要明确权限和规范。生产环境不是所有人都应该随便 attach JVM。通常需要通过堡垒机、审计账号、应急审批或 SRE 协同执行。涉及客户信息的入参和返回值不能随意打印,尤其是保单号、证件号、手机号、银行卡、理赔材料等敏感数据。

第二,要优先使用低风险命令。故障刚开始时可以先执行:

dashboard
jvm
memory
thread -n 5
thread -b

这些命令主要读取状态,风险相对可控。确认问题方向后,再对低频入口方法使用 tracewatch。例如导出接口本身 QPS 很低,可以观测一次;核心承保下单接口 QPS 很高,就要加条件表达式、限制次数,并避开打印大对象。

第三,使用 trace 时要从业务入口开始逐步下钻。比如:

trace com.xxx.policy.export.PolicyExportService export '#cost > 1000' -n 5

如果发现耗时集中在 queryPolicyList,再 trace 查询服务;如果集中在 buildExcel,再 trace Excel 写出方法;如果集中在 uploadFile,再查文件服务或网络。这样可以避免一次性增强过多方法。

第四,使用 watch 时要控制输出内容。例如只看集合大小,而不是打印整个集合:

watch com.xxx.policy.export.PolicyExportService export '{params[0], returnObj == null ? null : returnObj.size(), #cost}' '#cost > 1000' -n 3 -x 2

如果返回对象不是集合,可以只打印关键字段。保险系统里千万不要直接输出完整客户对象、保单明细列表、理赔影像元数据。

第五,用 jad 解决“代码是否一致”的争议。比如某个修复已经发布,但线上仍然报旧逻辑异常。可以:

jad com.xxx.claim.service.ClaimAuditService

查看实际运行的字节码逻辑,确认实例是否发布成功、类是否被旧包覆盖、是否存在多版本依赖冲突。

排查流程

第一步,先看全局状态。执行 dashboard,观察 CPU、线程、堆、GC、运行时间。如果 GC 次数和耗时快速增加,说明慢可能来自 STW;如果 CPU 高且 GC 不高,优先看业务线程;如果线程数异常上涨,要怀疑线程池、连接池、请求堆积。

第二步,看热点线程。执行 thread -n 5,找到 CPU 高的线程栈。如果栈里反复出现 JSON 序列化、正则匹配、Excel 写入、加解密、规则引擎,就往对应业务代码查。如果大量线程卡在数据库驱动或 HTTP 客户端,要结合下游监控。

第三步,看阻塞。执行 thread -b 或查看 BLOCKED 线程。如果大量线程等待同一把锁,说明可能存在同步块过大、本地缓存加载锁、单例初始化、文件写锁等问题。保险系统里常见的是字典缓存懒加载时加全局锁,高峰期大量请求排队。

第四步,用 trace 定位慢点。选择一个明确的业务入口方法,例如 PolicyQueryService.queryPageClaimSettleService.submitExportService.exportPolicy。加上耗时条件和次数限制,避免高频输出。观察子方法耗时占比,逐步下钻。

第五步,用 watch 验证参数和结果。比如怀疑导出没有分页,可以 watch 查询方法入参中的 pageSize;怀疑某个条件为空导致全表查询,可以 watch 查询 DTO;怀疑返回结果过大,可以 watch 返回 List 的 size;怀疑异常被吞掉,可以 watch thrown exception。

第六步,用 jad 核对实际代码。如果 trace 指向的代码与仓库理解不一致,或者某个实例行为和其他实例不同,用 jad 看实际加载类。必要时配合 sc -d 查看类加载器和来源 jar。

第七步,沉淀结论。Arthas 看到的是现场证据,最终要落成故障结论:哪个方法慢、为什么慢、影响哪些请求、临时止血怎么做、永久修复怎么做、需要补哪些日志和指标。

常见坑

第一个坑是对高频方法无条件 watch。如果方法每秒调用几千次,输出入参和返回值会给线上制造额外负担,甚至刷爆终端和日志。必须使用条件表达式和 -n 限制次数。

第二个坑是打印大对象。导出接口返回几十万行数据,如果直接 watch returnObj,诊断工具本身会参与制造内存和 CPU 压力。更好的做法是打印 size、关键字段和耗时。

第三个坑是把 trace 当链路追踪系统。Arthas 的 trace 适合短时间、点状、在线诊断,不适合长期替代 SkyWalking、Pinpoint、OpenTelemetry。线上治理仍然需要日志、指标和链路追踪。

第四个坑是忘记退出增强。使用后要 stop 或退出 Arthas,避免长期保留不必要的增强。虽然 Arthas 会尽量安全处理,但生产诊断要保持克制。

第五个坑是忽略敏感数据。保险系统的对象里经常带客户隐私,watch 表达式要只输出必要字段,诊断记录也要按敏感信息规范处理。

面试追问

面试官可能问:tracewatch 的区别是什么?为什么 watch 大对象危险?Arthas 怎么定位 CPU 高?怎么确认线上代码版本?如何查死锁或锁竞争?Arthas 会不会修改业务逻辑?线上使用 Arthas 有哪些安全规范?如果没有 Arthas,你会怎么排查?

还可能追问具体场景:导出接口慢你怎么 trace?查询结果为空你怎么 watch?某个实例和其他实例行为不同你怎么 jad?线程池满了你用 thread 能看到什么?Full GC 时 Arthas 能提供哪些辅助信息?

这些问题最好结合真实业务回答,而不是背命令参数。

推荐回答

可以这样回答:

我使用 Arthas 一般先用 dashboard 看 JVM 总体状态,包括线程、堆、GC 和 CPU。如果 CPU 高,就用 thread -n 找热点线程栈;如果接口慢,就从业务入口用 trace 看子方法耗时;如果怀疑参数或返回值异常,就用 watch 打印关键字段、集合大小和耗时;如果怀疑线上代码不一致,就用 jad 反编译实际加载的类。生产上我会加条件、限制次数,避免打印大对象和敏感字段。

有一次保单导出接口偶发超时,日志只能看到总耗时。我们用 trace 发现耗时不是数据库查询,而是导出前的字典和机构权限补全,某个缓存 miss 后串行加载非常慢。再用 watch 验证一次导出返回数据量很大,并且每条数据都触发了补全逻辑。后续把字典和机构信息批量预取,导出改为分页流式处理,同时补充了导出行数和阶段耗时指标。这个问题如果只靠补日志发版,会慢很多。

这个回答既说明了 Arthas 命令,也说明了风险控制和业务落地,面试可信度会高很多。

延伸学习路线

第一阶段,熟悉 Arthas 基础命令:dashboard、jvm、memory、thread、stack、trace、watch、jad、sc、sm、heapdump。每个命令都要知道适合什么问题。

第二阶段,学习 OGNL 表达式。重点掌握如何访问 paramsreturnObjthrowExp#cost,如何只打印集合大小和关键字段。

第三阶段,结合典型场景练习:CPU 高、接口慢、返回异常、代码版本不一致、线程阻塞、导出 OOM。每个场景写出命令组合和排查顺序。

第四阶段,学习生产安全规范。包括权限控制、敏感数据、条件表达式、次数限制、摘流诊断、诊断后退出增强。

第五阶段,把 Arthas 和监控体系结合。Arthas 擅长现场定位,长期治理仍然要靠指标、日志、链路追踪和告警。面试中能讲清楚这点,会显得更成熟。