Arthas 解决什么问题
线上接口突然变慢,日志里没有异常,重启后又恢复了。这种问题最难查:不能加日志重启,不能本地复现,只能对着一个正在运行的 JVM 猜。Arthas 通过 attach 机制挂到目标进程上,不重启、不改代码,就能看线程栈、方法耗时、入参返回值、类加载信息。
它的原理是动态 attach 到 JVM,利用 Instrumentation 做字节码增强。JDK 17 下 attach 机制仍然可用,但目标进程和 Arthas 必须用同一个用户启动,容器里还要注意 PID 命名空间。生产环境使用前要确认合规:Arthas 能读取方法参数和返回值,可能包含敏感数据,端口也不能直接暴露到公网。
命令地图
| 命令 | 用途 |
|---|---|
| dashboard | 总览线程、内存、GC、运行时 |
| thread | 查看线程栈、CPU 占用、死锁 |
| jad | 反编译已加载的类,确认线上代码版本 |
| watch | 观察方法入参、返回值、异常 |
| trace | 追踪方法内部调用耗时 |
| monitor | 周期性统计方法调用次数和耗时 |
| stack | 查看方法被谁调用 |
| tt | 记录调用现场,支持事后回放 |
| sc / sm | 查类、查方法 |
| ognl | 执行表达式,读取或修改对象 |
| heapdump | 导出堆转储 |
| profiler | 生成 CPU 火焰图 |
先启动:
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar --telnet-port 3658 --http-port 8563
启动后会列出当前用户的 Java 进程,输入序号 attach。端口只监听本地时更安全,需要远程访问就用 SSH 隧道,不要把 3658 直接开到公网。
CPU 高与慢方法
CPU 飙高时,先用 dashboard 看整体,再按线程排序:
dashboard
thread -n 5
thread -b
thread -n 5 列出 CPU 占用最高的五个线程,拿到线程 ID 后再 thread <id> 看完整栈。thread -b 专门找阻塞其他线程的锁持有者,死锁和长时间等待用它最快。
接口慢但 CPU 不高,通常是 IO 或锁等待,用 trace 定位耗时落在哪个方法:
trace com.example.order.OrderService create '#cost > 200' -n 5
#cost > 200 表示只记录耗时超过 200 毫秒的调用,-n 5 表示最多记录五次。不加限制地 trace 高并发方法,字节码增强本身就会拖慢应用。需要看具体参数和返回值时用 watch:
watch com.example.order.OrderService create '{params, returnObj, throwExp}' -x 2 -n 5
-x 2 是展开层级,层级越大输出越多,也越容易把日志打爆。线上排查建议先用 trace 找到慢方法,再用 watch 看特定条件的数据,不要反过来。
几个典型场景
方法返回值不符合预期:用 watch 看入参和返回值,确认是参数传错、还是下游返回异常。如果问题偶发,用 tt -t 记录多次调用,事后用 tt -i <index> -p 回放,不用一直盯着终端。
类版本不对:线上跑的还是旧包,jad com.example.OrderService 反编译看实际字节码,再 sc -d com.example.OrderService 看类的加载器和来源路径。多版本 jar 冲突时,classloader -t 能看到类加载器的层级关系。
日志级别临时调整:logger --name com.example.order --level debug 可以在不重启的情况下打开调试日志,排查完记得改回去。修改是运行时生效的,进程重启后恢复配置值。
线程池满、连接池耗尽:thread 看线程状态和栈,monitor -c 5 com.example.DbService query 统计方法调用频率和平均耗时。再结合 vmtool --action getInstances --className com.zaxxer.hikari.HikariDataSource --limit 10 拿到数据源实例,看连接池状态。
常见坑
1. attach 失败
常见原因是用户不一致、目标进程开启了 -XX:+DisableAttachMechanism、容器里 PID 命名空间隔离、或者 /tmp 权限不对。容器环境要把宿主机的 /tmp 挂进去,或者用 jattach 指定 PID。JDK 17 不再需要单独找 tools.jar,但 JVM 必须是 HotSpot。
2. trace 和 watch 开销过大
这两个命令都会做字节码增强,方法调用越频繁,开销越明显。一定要加 -n 限制次数,加条件表达式缩小范围,排查完执行 stop 或 shutdown 卸载增强。不要在生产对 QPS 上万的方法长期开 trace。
3. tt 把内存打满
tt 会保存调用现场的对象引用,记录越多占用越大。用 -n 限制条数,排查完执行 tt --delete-all。堆本来就紧张时,先别用 tt。
4. ognl 改坏了线上状态
ognl 能调用任意方法、修改静态字段,等于在进程里执行代码。只读场景也可能触发懒加载或副作用。生产环境优先用 watch、trace 这类观察命令,确需修改时先确认影响范围。
5. 把 jad 的反编译结果当成源码
反编译是近似还原,泛型、lambda、编译器优化后的代码可能和源码不一致。jad 用来确认“线上到底是哪个版本”,不要拿它当源码提交。
6. heapdump 打满磁盘
一次 dump 的大小接近堆使用量,磁盘空间不够会失败,甚至影响业务。导出前确认目录空间,dump 文件包含业务数据,注意权限和脱敏。
排查步骤
一个可复用的顺序是:先看整体,再定位线程,然后追方法,最后看数据。
dashboard
thread -n 5
trace com.example.order.OrderController getOrder '#cost > 500' -n 5
watch com.example.order.OrderService getOrder '{params, returnObj, throwExp}' -x 2 -n 5
stop
CPU 高先看 thread,接口慢先看 trace,结果不对先看 watch,偶发问题用 tt。每一步都带着明确假设,不要把所有命令都开一遍。排查结束后务必 stop,确认增强类已经卸载,避免长期影响性能。
小结
Arthas 的价值是把“重启加日志”变成“在线观察”,但它不是银弹。它擅长定位“哪个方法慢、参数是什么、线程卡在哪”,不擅长替代监控和链路追踪。生产使用要有审批、有超时、有清理,观察命令优先于修改命令。
评论