简介:Arthas 是阿里巴巴开源的 Java 诊断利器,v3.7.2 版本面向 Java 后端开发者、运维人员及计算机专业学生,用于在不重启应用的前提下完成线上问题定位、性能分析与运行时代码调试。资源包共 2000 个文件,约 10.76MB,以 894 个 md 文档、597 个 java 源码为主,辅以 png 截图、json 配置、vue 前端页面及 xml、properties、sh 等脚本与配置,完整呈现了 Arthas 的源码结构与文档体系。已有 127 人学习下载。借助其中的源码与说明,读者可深入理解命令行交互、热修复、类加载器查看、JVM 监控、SQL 执行分析、代码覆盖率集成等核心机制,并参考 Web 控制台与 IDE 插件的实现思路。对于毕业设计、计算机案例研究及系统软件开发,这份资料可作为研究 Java 运行时行为、排查复杂故障的实用参考,帮助缩短调试周期、提升代码质量。
1. 线上 Java 进程出问题,为什么我第一时间翻出这个 3.7.2 的压缩包
凌晨两点被告警叫醒,某个接口 P99 突然从 80ms 飙到 3s,日志里干干净净,重启能好但没人敢重启——这是很多 Java 后端都遇到过的场景。Arthas 就是为这种时刻准备的:一个开源的 Java 诊断工具,v3.7.2 这个版本把命令行诊断、字节码增强、类加载器排查、JVM 监控这些能力打包成一个可以直接 attach 到运行中进程的工具。它不需要你改一行业务代码,不需要重启应用,attach 上去就能看方法调用、看返回值、看异常、看耗时。这份压缩包里是完整的源码工程,包含as.bat、as-service.bat、batch.as、mvnw.cmd、control、jni-library.cpp、main.css这些构建与运行相关文件,适合想读源码、做二次开发、或者拿它当计算机案例和毕业设计素材的人。如果你只是想要一个能立刻用的诊断工具,官方发行包更省事;但如果你想搞清楚它怎么做到"不重启就改行为",这份源码值得拆。
2. Arthas 的字节码增强原理:为什么它能不重启就改方法行为
2.1 从 attach 机制到 Instrumentation
Arthas 能做到"不重启",核心靠的是 JVM 自带的两个能力:attach API 和 Instrumentation。当你执行arthas-boot或者用as.bat启动时,它实际上是通过VirtualMachine.attach(pid)把自己挂到目标 JVM 上,然后在目标进程里启动一个 agent。这个 agent 拿到Instrumentation实例后,就能调用retransformClasses对已经加载的类重新做字节码转换。
这里有个关键点:retransformClasses不是重新加载类,而是在原有类定义的基础上,把字节码再送进 ClassFileTransformer 链里过一遍。Arthas 的watch、trace、monitor、stack这些命令,本质上都是往目标方法里插入增强逻辑——在方法入口记录时间戳、在出口计算耗时、把参数和返回值塞进Advice对象,再通过一个独立的通信通道回传给 Arthas 客户端。整个过程对业务代码是透明的,方法签名不变,类加载器不变,所以不会触发LinkageError。
源码包里那个jni-library.cpp值得单独看一眼。Arthas 有一部分底层能力依赖 JNI,比如在某些 JDK 版本上做更精细的字节码操作或者获取更底层的运行时信息。C++ 这层不是必须的,但它解释了为什么 Arthas 在不同 JDK 上表现有差异——JNI 接口在不同 JDK 版本里的行为并不完全一致。
2.2 源码工程结构与构建入口
拿到这份源码,第一件事是搞清楚从哪进。压缩包里的文件大致分几类:
| 文件 | 作用 |
|---|---|
as.bat/as-service.bat | Windows 下的启动脚本,as-service偏向服务化启动 |
batch.as | 批处理脚本,用于批量执行 Arthas 命令 |
mvnw.cmd | Maven Wrapper,保证不装 Maven 也能构建 |
control | 控制脚本,管理进程生命周期 |
jni-library.cpp | JNI 层源码 |
main.css | Web Console 的样式文件 |
构建入口就是mvnw.cmd。在 Windows 上直接跑:
# 进入源码根目录后执行,-DskipTests 先跳过测试加快首次构建 mvnw.cmd clean package -DskipTests这条命令做三件事:clean清掉旧产物,package编译并打包,-DskipTests跳过单元测试。首次构建会下载大量依赖,建议配好国内镜像。构建完成后,target目录下会生成可执行的 jar 和发行包结构。
如果你在 Linux 或 macOS 上,把mvnw.cmd换成./mvnw即可,逻辑一样。注意mvnw依赖.mvn/wrapper目录下的配置,如果压缩包里这部分缺失,就得用本机 Maven 替代。
2.3 启动与 attach 到目标进程
构建出可运行产物后,启动方式有两种。一种是直接用as.bat:
# Windows 下启动 Arthas,会列出当前机器上的 Java 进程让你选 as.bat执行后会看到类似这样的进程列表:
* [1]: 12345 com.example.OrderApplication [2]: 23456 org.springframework.boot.loader.JarLauncher输入序号回车,Arthas 就 attach 上去了。另一种是直接指定 PID:
as.bat 12345attach 成功后进入交互式控制台,提示符变成[arthas@12345]$。这时候你敲的每一条命令,都是通过前面说的 Instrumentation 通道作用在目标进程上的。
提示:attach 失败最常见的原因是目标进程和 Arthas 用的 JDK 版本不一致,或者目标进程以不同用户身份运行。先确认
java -version和进程启动用户。
3. 核心诊断命令实战:watch、trace、jad 怎么配合用
3.1 watch 命令:看参数、看返回值、看异常
watch是日常用得最多的命令。它的作用是观察指定方法的调用情况,可以看入参、返回值、抛出的异常,还能控制输出深度和次数。
# 观察 OrderService 的 createOrder 方法,看入参和返回值,最多输出 5 次 watch com.example.service.OrderService createOrder '{params, returnObj}' -x 2 -n 5参数逐个拆:com.example.service.OrderService是全限定类名,createOrder是方法名,{params, returnObj}是 OGNL 表达式,表示要打印的参数和返回值。-x 2控制对象展开深度为 2 层,太深会刷屏,太浅看不到关键字段。-n 5表示最多命中 5 次就停,不加这个参数会一直输出。
如果想看异常:
# 观察方法抛出的异常信息 watch com.example.service.OrderService createOrder '{params, throwExp}' -e -x 2-e表示只在方法抛出异常时输出。这个在排查"为什么这个接口偶尔报错"时特别有用,因为异常可能被上层 catch 掉了,日志里看不到。
3.2 trace 命令:定位耗时瓶颈
watch看的是数据,trace看的是时间。当接口变慢但不知道慢在哪一层时,trace能打出方法调用链上每个节点的耗时。
# 追踪 createOrder 的调用路径,只显示耗时超过 50ms 的节点 trace com.example.service.OrderService createOrder '#cost > 50' -n 3#cost > 50是条件表达式,单位是毫秒,只输出耗时超过 50ms 的调用节点。-n 3限制输出 3 次。输出会是一棵树,每个节点显示类名、方法名、耗时百分比。你一眼就能看出是数据库查询慢、还是某个 RPC 调用慢、还是循环里做了重复计算。
有个坑要注意:trace本身有性能开销,在高频方法上开trace会让接口更慢。所以生产环境用trace一定要加-n限制次数,排查完立刻stop。
3.3 jad 命令:反编译看真实运行的代码
线上跑的代码和你本地看的代码不一致,这种事不罕见——可能是分支搞错了,可能是热部署残留,可能是依赖版本冲突。jad能把 JVM 里实际加载的字节码反编译成 Java 源码。
# 反编译指定类,看实际运行的代码 jad com.example.service.OrderService输出就是反编译后的源码。如果发现和你本地代码对不上,那问题就找到了。jad还可以配合--source-only只输出源码,方便重定向到文件对比。
watch、trace、jad这三个命令的组合拳是:先用jad确认代码版本对不对,再用trace定位慢在哪,最后用watch看具体数据。三步下来,大部分线上疑难杂症都能收敛。
4. 避坑与常见问题:attach 失败、命令无输出、性能反噬
4.1 attach 报 "com.sun.tools.attach.AttachNotSupportedException"
现象:执行as.bat后选进程,报 attach 异常,进不去控制台。
原因:Arthas 依赖tools.jar(JDK 8)或jdk.attach模块(JDK 9+)。如果你用的是 JRE 而不是 JDK,或者 JDK 版本和 Arthas 期望的不匹配,attach 就会失败。另一个常见原因是目标进程和 Arthas 运行在不同用户下,权限不够。
解决:确认JAVA_HOME指向 JDK 而非 JRE;确认 Arthas 和目标进程用同一个 JDK 大版本;Linux 下用ps -ef看进程属主,必要时用相同用户启动 Arthas。
4.2 watch/trace 命令执行后没有任何输出
现象:命令敲下去,提示Affect(class count: 1, method count: 1)但就是不打印数据。
原因:方法没被调用到。可能是你观察的方法名写错了(重载方法要带参数类型),可能是类加载器不对(同一个类被多个 ClassLoader 加载),也可能是条件表达式过滤掉了所有调用。
解决:先用sc com.example.service.OrderService确认类被加载了,用sm com.example.service.OrderService确认方法签名。如果类被多个 ClassLoader 加载,用-c参数指定 ClassLoader。条件表达式先去掉,确认能出数据再加过滤。
4.3 trace 之后接口变得更慢
现象:开了trace排查慢接口,结果接口从 3s 变成 8s。
原因:trace会在每个调用节点插入增强逻辑,方法调用越频繁、调用链越深,开销越大。在高 QPS 接口上不加限制地开trace,等于给系统加了一层全链路埋点。
解决:永远加-n限制次数,排查完立刻stop或shutdown。如果接口 QPS 很高,考虑用monitor代替trace,monitor是周期性统计,开销小得多。
4.4 热修复改了代码但没生效
现象:用redefine或retransform改了类,但行为没变。
原因:redefine不能改方法签名、不能加字段、不能改继承关系,只能改方法体。如果你改的内容超出了这个范围,JVM 会拒绝。另外,如果类被多个 ClassLoader 加载,你只改了其中一个。
解决:确认改动只在方法体内部;用classloader命令看目标类被哪些 ClassLoader 加载;改完后用jad确认字节码确实变了。
4.5 Web Console 打不开或样式错乱
现象:启动web-console后浏览器访问空白或样式丢失。
原因:源码包里的main.css是 Web Console 的样式文件,如果构建时静态资源没打包进去,或者端口被占用,就会出问题。
解决:确认构建产物里包含静态资源;检查web-console监听的端口是否被防火墙拦截;换一个端口启动。
5. 从源码到进阶:用 classloader 和 jvm 命令做深度排查
5.1 classloader 命令排查类冲突
类冲突是 Java 里最玄学的问题之一:同一个类被不同 ClassLoader 加载了两份,instanceof返回 false,ClassCastException报得莫名其妙。classloader命令能把这层黑匣子打开。
# 查看所有 ClassLoader 的统计信息 classloader # 查看某个类的加载来源 classloader -c 3d4e5f6a -l com.example.service.OrderService第一条命令输出所有 ClassLoader 的层级、加载类数量、父加载器。第二条命令中-c指定 ClassLoader 的 hash,-l列出该 ClassLoader 加载的指定类。如果同一个类出现在两个 ClassLoader 下,冲突就实锤了。
常见场景是 Tomcat 热部署后旧 ClassLoader 没释放,或者 OSGi、SPI 场景下多个加载器各加载一份。找到之后,要么统一加载器,要么用-c指定正确的那个。
5.2 jvm 命令看内存、线程、GC
jvm命令一次性输出 JVM 的运行时快照:
# 查看 JVM 信息,包括内存、GC、线程、系统属性 jvm输出里重点看几块:HEAP-MEMORY-USAGE看堆内存使用和 GC 次数,THREAD-COUNT看线程数是否异常增长,GC看 Full GC 频率。如果 Full GC 频繁但堆内存不高,可能是元空间或直接内存的问题,这时候配合memory命令看非堆区域。
thread命令配合使用:
# 查看最忙的 5 个线程的堆栈 thread -n 5 # 查看死锁 thread -bthread -n 5打出 CPU 占用最高的 5 个线程堆栈,直接定位到热点代码。thread -b检测死锁,有死锁会直接告诉你哪两个线程在互相等锁。
5.3 火焰图与性能分析
Arthas 支持生成火焰图,底层是 async-profiler。用法:
# 采样 30 秒,生成火焰图 profiler start --duration 30 profiler stop --format html --file /tmp/flamegraph.htmlprofiler start开始采样,--duration 30表示采 30 秒后自动停止。profiler stop输出结果,--format html生成可交互的 HTML 火焰图,--file指定输出路径。火焰图的横轴是采样占比,纵轴是调用栈,越宽的方法占用 CPU 越多。这是定位 CPU 热点的最直观方式,比trace更适合高频方法的性能分析。
5.4 一个我踩过的坑
有次排查一个偶发的ClassCastException,本地怎么都复现不了。用classloader一看,发现同一个 DTO 类被两个 ClassLoader 各加载了一份,一个是应用加载器,一个是某个中间件自带的加载器。两个类名字一样、字段一样,但 JVM 认为是两个不同的类。最后是在中间件配置里排除了那个类的重复打包才解决。
从那以后我每次遇到"类名一样但类型不匹配"的问题,都强制先走一遍classloader命令,确认类的加载来源,再往下查。这个习惯帮我省了很多瞎猜的时间。希望这份源码包和上面的排查思路,能帮你在下次线上告警时少熬一个通宵。
本文还有配套的精品资源,点击获取