Arthas是一款线上监控诊断产品,通过全局视角实时查看应用load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。
当遇到以下类似问题而束手无策时,Arthas可以为你提供帮助:
- 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
- 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
- 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
- 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
- 是否有一个全局视角来查看系统的运行状况?
- 有什么办法可以监控到 JVM 的实时运行状态?
- 怎么快速定位应用的热点,生成火焰图?
- 怎样直接从 JVM 内查找某个类的实例?
Arthas是一个Java应用,它启动后能够自动搜索本机部署的Java进程,并可以作为一个观察者持续观察我们的Java进程。Arthas作为观察者能够实时监控大整个JVM运行状态信息,小到类的运行状态,方法的运行状态信息。除此之外,Arthas能够让我们在不停机的情况下实现代码热更新,以及动态开启日志等功能,这对我们排查线上问题十分有用。
演示环境
Arthas支持 JDK 6+(4.x 版本不再支持 JDK 6 和 JDK 7),支持 Linux/Mac/Windows.
启动测试程序
curl -O https://arthas.aliyun.com/math-game.jar
java -jar math-game.jar
math-game是Arthas官网提供的一个普通Demo,用于测试Arthas的功能。源码如下:
package demo;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class MathGame {
private static Random random = new Random();
private int illegalArgumentCount = 0;
public static void main(String[] args) throws InterruptedException {
MathGame game = new MathGame();
// 无限循环生成
while (true) {
game.run();
TimeUnit.SECONDS.sleep(1);
}
}
public void run() throws InterruptedException {
try {
// 生成随机数
int number = random.nextInt()/10000;
// 获取分解质因数结果
List<Integer> primeFactors = primeFactors(number);
// 打印分解质因数结果
print(number, primeFactors);
} catch (Exception e) {
System.out.println(String.format("illegalArgumentCount:%3d, ", illegalArgumentCount) + e.getMessage());
}
}
//打印方法
public static void print(int number, List<Integer> primeFactors) {
StringBuffer sb = new StringBuffer(number + "=");
for (int factor : primeFactors) {
sb.append(factor).append('*');
}
if (sb.charAt(sb.length() - 1) == '*') {
sb.deleteCharAt(sb.length() - 1);
}
System.out.println(sb);
}
// 根据传入参数对其分解质因数,返回分解质因数列表
// 比如传入参数为3 返回值为【3】
//传入参数为10 返回值为【2,5】
// 传入参数小于2 ,抛出 IllegalArgumentException异常
public List<Integer> primeFactors(int number) {
if (number < 2) {
illegalArgumentCount++;
throw new IllegalArgumentException("number is: " + number + ", need >= 2");
}
List<Integer> result = new ArrayList<Integer>();
int i = 2;
while (i <= number) {
if (number % i == 0) {
result.add(i);
number = number / i;
i = 2;
} else {
i++;
}
}
return result;
}
}
这个程序逻辑很简单:main()方法为入口,一个while循环无限执行run()方法,每次执行完睡眠一秒。run()方法内部首先生成一个随机数,然后调用primeFactors()方法对随机数分解质因数,最后调用print()方法打印结果。
启动demo后,会在控制台持续打印类似如下的信息:

每一行代表一次执行结果,例如第一行提示参数非法,当前参数为-200534,需要大于2;第三行打印的是209949分解质因数的结果。
启动Arthas
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
启动后,控制台会列出检测到的Java进程:

输入目标Java程序前面对应的序号(这里是1)并回车,Arthas便会附着到该进程上。成功连接后,会看到以下界面:

至此,测试环境准备就绪。
Arthas常用命令
提示:必须在Arthas成功连接目标程序后,才能执行其命令。Arthas支持Tab键命令补全。
help
帮助命令,列出所有可用命令及其简介。

-h
几乎所有Arthas命令都支持-h参数,用于查看该命令的详细用法。

也可以直接查阅Arthas官网的中文命令文档:https://arthas.aliyun.com/doc/commands.html
dashboard
仪表盘命令,用于从整体视角展示系统状态统计信息,包括线程、内存、GC和运行时数据。

常用参数:
-i 刷新实时数据的时间间隔 (ms),默认 5000ms
-n 刷新实时数据的次数
例如,dashboard -i 2000 -n 2 表示每隔2秒刷新一次,只刷新两次后自动结束。
thread
查看线程相关信息。
thread 显示第一页线程信息。

thread --all 显示所有线程信息。
thread id 显示指定线程ID的堆栈信息。
thread -n 3 查看最忙的三个线程堆栈信息。
thread -b 检测死锁(目前主要支持 synchronized 关键字导致的死锁)。
sc (Search Class)
搜索JVM已加载的类信息。
sc -d demo.MathGame #查看MathGame类的详细信息,-d代表详细信息

sm (Search Method)
搜索JVM已加载的方法信息。
sm -d demo.MathGame #查看MathGame类的方法详细信息

jad
反编译命令,将指定的类或方法反编译为Java代码。用于确认线上运行的代码版本。
jad demo.MathGame 反编译MathGame类。

jad demo.MathGame primeFactors 仅反编译primeFactors方法。
jad demo.MathGame --source-only 仅输出源码,不包含类加载器信息。
mc (Memory Compile)
内存编译,类似于javac命令,将.java文件编译为.class文件。
mc /path/to/Some.java -d /output/dir
redefine
重新定义类,将外部的.class文件热加载到JVM中,实现代码热更新。
redefine /path/to/Some.class
注意redefine的限制:
- 不能修改、添加、删除类的字段和方法(包括方法签名)。
- 正在执行的方法不会立即生效。
- 与
watch/trace/jad/tt等命令可能存在冲突。
jad | mc | redefine 实现热更新
这三个命令组合可以实现无需重启的代码热更新。
- 反编译线上类到文件:
jad demo.MathGame --source-only > /tmp/MathGame.java
- 编辑
/tmp/MathGame.java文件,修改代码。
- 编译修改后的Java文件:
mc /tmp/MathGame.java -d /tmp/
- 重新加载编译后的Class文件到JVM:
redefine /tmp/demo/MathGame.class
执行成功后,线上程序的逻辑会立即更新。
watch
观察方法的执行情况,查看入参、返回值、异常、对象属性等。
基本语法:watch 全限定类名 方法名 “观察表达式”
例如,观察primeFactors方法的参数、目标对象、返回值和异常:
watch demo.MathGame primeFactors '{params, target, returnObj, throwExp}'

观察表达式使用OGNL语法。添加 -x 参数可以指定对象展开层级(默认为1):
watch demo.MathGame primeFactors '{params, target, returnObj, throwExp}' -x 2
更多用法请参考官方文档:https://arthas.aliyun.com/doc/watch.html
trace
追踪方法内部调用路径,并输出每个节点的耗时,用于性能瓶颈定位。
trace demo.MathGame run

默认不包含JDK方法,添加--skipJDKMethod false可以包含。更多用法:https://arthas.aliyun.com/doc/trace.html
tt (Time Tunnel)
时间隧道命令。记录下指定方法每次调用的现场信息(参数、返回值、异常等),并可对这些记录进行事后查看甚至重放。
tt -t demo.MathGame run 开始记录run方法的调用。

tt -l 列出所有记录。
tt -i 1000 查看索引为1000的记录的详细信息。
tt -p -i 1000 重放索引为1000的方法调用。
tt -d -i 1000 删除指定记录。
tt --delete-all 清除全部记录。
heapdump
生成JVM堆内存转储文件(类似jmap -dump功能)。
heapdump /tmp/dump.hprof

生成的.hprof文件可以使用MAT(Memory Analyzer Tool)等工具进行分析,排查内存泄漏等问题。注意:此操作可能对应用性能产生影响,建议在业务低峰期执行。
总结
Arthas是一个功能强大的Java诊断工具,能够帮助开发者在不重启应用的前提下,高效地排查线上问题、监控应用状态、定位性能瓶颈。本文仅介绍了部分核心命令,Arthas官方提供了多达45个命令,更多高级功能和详细用法,请参考Arthas官方文档。熟练掌握Arthas,将极大提升你的线上运维和问题排查能力。