Arthas 线上排障时,我最常先敲的不是 6 个命令,而是两条确认路径
Arthas 真正好用的地方,不是背会 6 个命令,而是在线上先判断自己现在要确认的是线程在忙什么,还是某个方法为什么慢。把 `dashboard`、`thread`、`trace`、`watch`、`stack`、`jad` 放回两条真实排障路径里,命令边界会清楚很多。
很多人学 Arthas,都是从命令表开始记:
dashboard看全局thread看线程trace看耗时watch看参数stack看调用来源jad看线上代码
这些都没错,但真到线上现场,问题往往不在于你记不记得这 6 个名字,而在于你不知道自己现在到底在确认哪一种怀疑。
于是现场很容易变成这样:
- 先
dashboard - 再
thread -n 5 - 然后忍不住对几个方法都
trace - 觉得返回值也可疑,再开
watch - 最后材料很多,判断还是没收住
我后来慢慢发现,Arthas 真正值钱的地方,不是“有 6 个高频命令”,而是它能把原本散落在不同工具里的确认动作,压进同一个交互现场里。
但前提是:不要把它当命令大全来用,要把它当排障路径里的取证工具来用。
所以这篇不再按 6 个命令逐个铺开,而是只讲我在线上更常走的两条路径:
- 服务突然慢了,但我还不知道慢在全局压力、线程等待,还是某个业务方法
- 代码行为和预期不一样,我怀疑不是“性能问题”,而是参数、调用来源或线上版本本身有偏差
那 6 个命令都会出现,但它们出现的顺序,会更像真实现场。
一、第一条路径:接口突然变慢,我先判断“线程在忙什么”,再判断“方法为什么慢”
这条线是我用 Arthas 最多的场景。
告警通常长这样:
- 某个服务 RT 抬高
- 错误率没明显爆
- CPU 也许高,也许不高
- 但值班群里已经开始问是不是 Java 进程卡住了
这时候如果一上来就 trace 某个 Service 方法,通常有点早。
因为你还没回答一个更基础的问题:
现在到底是整机 / JVM 已经很紧,还是某些线程卡住了,还是业务方法本身真的慢?
1. 我会先用 dashboard 看一眼全局,不是为了找根因,而是为了防止方向跑偏
dashboard
我最关心的不是把屏幕上每一块都解释一遍,而是先用它排掉几种很粗的误判:
- heap、GC 看起来是不是已经明显不对
- 线程数有没有异常抬高
- system load 是不是已经很夸张
- top threads 里能不能直接看出热点线程
dashboard 在这里的价值很像“先抬头看病人是不是已经全身发热”,而不是直接判断病因。
如果这一步已经能看出 JVM 整体在高压,比如线程数抬得很厉害、load 也高,那我后面会更警惕线程堆积、锁等待、外部调用卡住这些方向。
如果这一步看起来并不夸张,那说明问题也许更集中在少数链路,而不是整个 JVM 都在喘。
2. 接下来我通常直接去 thread,因为线上很多“慢”最后都是线程先把真相露出来
thread -n 5
这一步比很多人想的更关键。
因为值班现场里,大家说的“慢”常常是不同东西:
- 有的是 CPU 真高
- 有的是线程卡在锁上
- 有的是线程在等数据库
- 有的是线程在等下游 HTTP
- 还有的是线程并不忙,只是请求都排在池子里
thread 最值钱的地方,就是它会逼你先回答一句很朴素的话:
这些线程现在到底是在跑,还是在等?
如果我看到高 CPU 线程一直停在 JSON 序列化、对象转换或者某段循环里,那后面很可能会沿热点方法继续收。
如果我看到一批线程都停在 JDBC、连接池或某个 RPC client 上,那我通常不会急着把矛头指向 Java 代码本身,因为这更像外部等待把请求时间拖长了。
如果我看到线程栈高度一致,反复卡在同一段业务调用上,那就说明 trace 的目标已经差不多有了。
这也是我为什么很少把 thread 当成“CPU 高时才用”的命令。在线上,它更像是我判断“慢的形态”的第一把刀。
3. 只有当可疑方法已经收得比较窄时,我才会上 trace
trace com.example.OrderService createOrder
很多人用 Arthas 时最容易犯的错,就是把 trace 当成雷达乱扫。
但 trace 真正值钱的前提,是你已经有了一个相对具体的怀疑:
- 我怀疑慢点就在这个 Service 方法里
- 或者我已经知道线程老停在这个 DAO / client 调用附近
- 我现在想确认到底是方法内部哪一段在吃时间
它回答的是:
这段调用链里,耗时到底沉在谁身上?
我更常见到两种结果。
第一种,是 trace 很快把锅收窄到下游调用上,比如数据库查询或某个 RPC client。这种情况下,Arthas 已经帮我完成了工作的一半:至少我知道不用继续在本地业务分支里翻来翻去。
第二种,是 trace 发现真正慢的是本地逻辑,比如组装大对象、循环处理、聚合计算,或者某个缓存 miss 后的回源分支。这种时候,继续沿代码看就有意义了。
但无论是哪一种,我都会提醒自己:trace 给的是耗时结构,不自动等于根因。
比如数据库调用慢,可能是 SQL 真慢,也可能是连接池排队;本地方法慢,也可能只是被上游放大的结果。它帮你把怀疑压到一层,但不会替你做最后的归因。
4. 如果我发现“慢”并不稳定,而是怀疑某类参数才会触发坏路径,这时才轮到 watch
watch com.example.OrderService createOrder '{params,returnObj,throwExp}' -x 2
watch 不是我在性能现场第一时间就会开的命令。
因为很多性能问题,先知道参数长什么样,并不能马上帮助你判断方向。
但当现场出现下面这几种迹象时,watch 就很有价值:
- 不是所有请求都慢,只有某一类请求慢
- 同一个方法平时正常,偶尔会走到很奇怪的分支
- 你怀疑开关值、配置值、入参形态和你想的不一样
- 你怀疑异常被吞了,或者返回值其实已经走了兜底
这一步更像是拿实样,不是看趋势。
我会尽量只看最关键的几个字段,不让线上输出变成一面瀑布墙。watch 当然强,但它最容易把人带进“既然能打印,那就多看一点”的冲动里。真到高压现场,克制比功能更重要。
5. 这条路径里,stack 和 jad 往往不是第一批出场,但经常负责最后的坐实
如果 trace 和 watch 之后,我还是有两个典型疑问,Arthas 还有后手。
一个疑问是:
这个方法到底是谁调进来的?
这时我会用:
stack com.example.OrderService createOrder
它特别适合处理那种“我知道这个方法有问题,但我不确定是谁把它带进主链路”的场景。
真实现场里,这种误判特别常见。你以为是 Controller 主链路调的,结果其实是异步补偿任务、定时任务或者 MQ 消费者调的。只要调用来源认错,后面的判断就很容易跟着歪。
另一个疑问是:
线上跑的,真的是我脑子里那份代码吗?
这时我才会上:
jad com.example.OrderService
jad 很像最后那一下确认。很多线上排障其实不是技术难,而是你默认了几个前提:
- 这个版本已经发上去了
- 这个 if 分支现在一定是这样走的
- 这个灰度实例跟别的实例是一致的
可只要这些前提里有一个是错的,前面看再多线程、参数、耗时都可能白忙。jad 不是高频乱用的命令,但一旦怀疑“线上代码和本地认知不一致”,它会非常省时间。
二、第二条路径:现象像 bug,不像纯性能问题时,我会先确认“是不是我理解错了现场”
还有一类现场,也很适合 Arthas,但起手式和前一条线不一样。
它的典型描述不是“服务慢了”,而是:
- 同一段逻辑在测试环境没问题,线上却偶发异常
- 某个分支明明不该进,线上却进了
- 某个开关明明应该生效,结果像没生效
- 返回值看起来不对,但日志又没完整打出来
这类问题如果还按“全局 -> 线程 -> trace”的节奏走,有时会比较慢。因为你真正怀疑的,往往不是耗时,而是现场事实和你的认知有没有对上。
1. 我通常先用 watch,先确认参数、返回值和异常是不是我以为的那样
如果我怀疑某种参数组合把代码带进了坏路径,或者返回值和日志描述对不上,watch 往往是最快的。
它解决的不是“哪里最慢”,而是:
- 线上真实参数到底长什么样
- 返回值是不是提前兜底了
- 异常是不是根本没冒到日志那层
这一步一旦确认错了,很多争论会直接结束。
2. 然后我会用 stack 看调用来源,因为线上 bug 经常不是代码不会跑,而是入口和你想的不一样
同一个方法,被 Controller 调、被定时任务调、被 MQ 消费调,现场含义完全不一样。
如果你只看方法体,不看谁调了它,就很容易把“入口错了”误判成“逻辑错了”。
stack 在这种时候特别实用,因为它补的是调用背景。
3. 最后再用 jad 把线上类本身确认掉
我吃过几次亏以后,现在只要现场出现下面任一信号,就会很自然想到 jad:
- 大家都说“代码明明改过了”
- 某个逻辑只在一部分实例上表现异常
- 配置和源码对不上
- 你越看越觉得线上行为不像当前分支
这种时候,去争论“按理说”没什么意义,直接看线上类更快。
所以在这条路径里,Arthas 更像一个“事实校验器”:
watch校验输入输出stack校验调用来源jad校验线上实现
三、这 6 个命令我怎么记:不是按功能背,而是按问题背
如果非要把这篇里出现的 6 个命令各记一句,我现在更习惯这样记:
dashboard:先看现场是不是已经整体失衡thread:先搞清线程到底在跑还是在等trace:可疑方法收窄后,再拆耗时结构watch:怀疑参数、返回值、异常不对时,直接看样本stack:怀疑调用入口认错了时,用它补背景jad:怀疑线上版本或实际实现不对时,用它做最后确认
你会发现,这样记之后,它们就不再是平铺的 6 个功能点,而是两条排障路径里的 6 个证据位置。
四、Arthas 最常见的误用,不是不会敲命令,而是太想一步到位
我自己在线上最常提醒自己的,反而不是某个参数怎么写,而是下面几件事。
1. 不要一接上 Arthas 就把能开的都开一遍
命令多,不代表证据更完整。
如果你还没想清自己现在在确认什么,开得越多,越容易把注意力带散。
2. trace 很强,但前提是目标已经相对明确
它不适合做大范围盲扫,更不适合当“总会告诉我答案”的万能入口。
3. watch 很容易让人上头
线上能看到参数和返回值当然很爽,但如果对象太大、方法太高频,现场很快就会被噪音淹掉。
4. 很多“性能问题”最后不是靠耗时图坐实,而是靠调用来源和线上代码确认掉
这也是为什么 stack 和 jad 看起来不如 trace 那么“炫”,但在真实现场里经常特别省时间。
五、如果你只想先把一套顺序记住,我建议记这一版
我自己比较常用的起手顺序是这样的。
现场像“慢了,但方向不清”
dashboardthread- 怀疑点收窄后
trace - 只有在参数、异常、开关值可疑时再补
watch - 调用来源不清时补
stack - 怀疑版本或实现不一致时补
jad
现场像“行为不对,不像纯性能问题”
watchstackjad- 如果过程中发现其实是方法耗时异常,再回头补
trace
Arthas 真正好用的地方,不是你背下来“6 个最有用的命令”,而是你能更快分清:
- 我现在是在找慢点
- 还是在找线程状态
- 还是在确认输入输出
- 还是在确认是谁调进来的
- 还是在确认线上到底跑了什么代码
当这件事分清之后,命令本身就没那么难记了。