什么是Arthas?Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。
Arthas 是 Alibaba 开源的 Java 诊断工具,深受开发者喜爱。
当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:
Arthas是一个java应用,它启动后能够自动搜索本机部署的java进程,并可以作为一个观察者持续观察我们的java进程,Arthas作为观察者能够实时监控大整个JVM运行状态信息,小到类的运行状态,方法的运行状态信息。除此之外,Arthas能够让我们在不停机的情况下实现代码热更新,以及动态开启日志等功能,这对我们排查线上问题十分有用。
Arthas的使用演示环境
jdk1.8centos7
Arthas 支持 JDK 6+(4.x 版本不再支持 JDK 6 和 JDK 7),支持 Linux/Mac/Windows.
启动测试程序
curl -O https://arthas.aliyun.com/math-game.jarjava -jar math-game.jar
math-game是Arthas官网提供的一个普通Demo,用于测试Arthas的功能,其实启动任何java程序都可,使用官方实例因为懒得写。math-game源码:
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;
}
}
math-game代码只是一个简单程序,作用是分解质因数。main()方法为入口,入口处一个while循环无限执行run()方法, 执行完后睡眠一秒,run() 方法内部逻辑分为三步,第一步生成一个随机数,第二步随机数作为参数通过调用primeFactors()方法获取分解质因数结果,第三步调用print()方法打印结果。启动demo后,会在控制台持续打印如下信息:

每一行代表一次执行结果打印,比如第一行打印信息提示违法参数数量为1,当前参数为-200534,需要大于2,第三行打印209949分解质因数的结果为3*47*1489.
启动Arthas
curl -O https://arthas.aliyun.com/arthas-boot.jarjava -jar arthas-boot.jar
启动后控制台出现如下打印:

第三行告诉我们arthas找到一个java进程,并在下方以列表形式展示了出来,然后我们只需要在控制台输入Java程序前面对应的序号,点击回车,arthas即可attach(黏附)到目标java应用上,实现对目标java程序的监控,我这里序号是1,输入后点击回车等待,出现以下界面代表arthas已经连接上我们选择的Java程序了:

现在测试环境已经准备好了,接下来介绍arthas的常用命令。
Arthas常用命令提示:Arthas的命令必须在Arthas连接上目标程序才可执行。Arthas支持命令补全,输入命令前面部分字母,按下tab键,可以自动补全命令、
help
帮助命令,输入后会展示arthas所有可用命令以及命令介绍。

左边绿色字体代表命令,右边白色字体为命令描述。-h-h并不是一个命令,它是Arthas所有命令都有的参数,-h参数也是帮助命令,当输入任意命令后带上一个【-h】参数,能够展示出这个命令更详细的用法介绍。

当然,上面的文档都是英文的,不利于阅读,我们可以使用翻译软件辅助阅读,也可以直接看Arthas官网对这些命令的帮助文档,全是中文编写,更利于我们理解:
https://arthas.aliyun.com/doc/commands.html
一些常用命令

dashboard
顾名思义,仪表盘,一般用于以整体视角和维度展示系统的一些状态统计信息。

输出结果分为三部分,第一部分展示线程信息,第二部分展示内存和GC信息,第三部分展示部分JVM运行时变量信息。常用参数:
-i 刷新实时数据的时间间隔 (ms),默认 5000ms-n 刷新实时数据的次数
直接输入【dashboard】命令命令行界面会每隔5秒重新刷新一次采集数据信息,无限循环,可以按ctrl+c退出,输入【dashboard -i 2000 -n 2】效果为刷新实时数据间隔为2s,一共只刷新两次自动结束。thread查看线程相关信息
thread #显示第一页线程信息

thread --all #显示所有线程信息

thread id #显示对应线程id的堆栈信息,id为上面显示的ID列的值。
上面可以查看线程ID为1的线程为main方法所在线程

thread -n 3 #查看最忙的三个线程堆栈信息

thead -b #查看当前java进程是否存在死锁

注意, 目前只支持找出 synchronized 关键字阻塞住的线程, 如果是java.util.concurrent.Lock, 目前还不支持。
sm(Search Method),搜索jvm已经加载的方法信息
sm -d demo.MathGame #查看MathGame类的方法详细信息基本语法:
sm -d 全限定类名 #查看MathGame类的方法详细信息 ,-d代表详细信息
sm -h #帮助命令,查看更详细用法

每一部分展示一个方法的详细信息,返回值解释:
declaring_class: 代表声明的类。
constructor_name: 构造方法名。
modifer: 方法修饰符。
anotation: 方法存在的注解
parameters: 方法参数。
exceptions: 方法抛出的异常。
classLoaderHash: 方法对应的类加载器的hash码。
sc
(Search Class),搜索jvm已经加载的类信息.
sc -d demo.MathGame #查看MathGame类的详细信息
基本语法: sm -d 全限定类名 #查看MathGame类的详细信息 ,
-d代表详细信息
sm -h #帮助命令,查看更详细用法

执行结果解释:
class-info: 类信息。
code-source: 类所在jar包。
name: 全限定类名。
isInterface: 是否为接口。
isAnnotation: 是否为注解。
isEnum: 是否为枚举类。
isAnouymousClass: 是否匿名类。
isArray:是否为数组。
isMemberClass: 是否成员类。
simple-name: 类名。
modifier: 类修饰符。
annotation: 类存在的注解。
interfaces: 类实现的接口。
super-class: 该类的父类。
class-loader: 类加载器。
classLoaderHash: 类加载器hash码。
jad
反编译命令,能将class类或者方法反编译为java代码,我们有时候修改代码发版发现修改的功能没有达到预期,不确定到底是没提交代码还是修改的功能不对就可用jad反编译功能排查。反编译某个类和方法查看代码是否已发布。
jad demo.MathGme #反编译类,这里类使用全限定类名

反编译第一部分显示类加载器信息,第二部分显示该类所在jar包位置,第三部分显示反编译的源码,源码部分还会显示对用行号。
如果只想显示源码,可以使用添加 --source-only 参数。
jad demo.Math primeFactors #反编译math-game中的分解质因数方法。

mc 内存编译,类似于jdk中的javac命令,能够把.java文件编译为.class文件。
mc java文件 -d 目录 #将java文件编译成class文件并输出到指定目录
mc -h # 查看更详细用法
详细用法稍后结合一个案例一起演示。
redefine class文件 #将class文件加载到JVM内存中redefine -h # 查看更详细用法详细用法稍后结合一个案例一起演示。
jad demo.MathGame --source-only > /lyc/MathGame.java#只反编译MathGame 类源码并将内容输入到MathGame.java文件中。


mc /lyc/MathGame.java -d /lyc# 将MathGame.java文件编译为class文件输出到lyc目录中

redefine /lyc/demo/MathGame.class# 将MathGame类加载进JVM内存中


redefine 的 class 不能修改、添加、删除类的 field 和 method,包括方法参数、方法名称及返回值
目前 redefine 和 watch/trace/jad/tt 等命令冲突,以后重新实现 redefine 功能会解决此问题

https://arthas.aliyun.com/doc/retransform.htmlwatch作用是观察方法的入参、返回值、异常、实例对象本身等信息。基本语法:watch 全限定类名 方法名 【观察表达式】比如我想查看primeFactors 分解质因数方法的【请求参数,MathGame对象实例,返回值,异常】四种信息。watch demo.MathGame primeFactors '{params, target, returnObj, throwExp}'观察表达式写法其实是使用的ognl表达式,关于ognl表达式的基本用法,可以自行百度。此处{params, target, returnObj, throwExp}就代表观察表达式,代表观察指定方法的【请求参数,MathGame对象实例,返回值,异常】四种信息, 加上 ‘{}’ ,代表这四个对象信息以一个ArrayList对象返回。

https://arthas.aliyun.com/doc/watch.htmltrace 全限定类名 方法名我们以监控run方法耗时为例。trace demo.MathGame run
`---ts=2025-10-20 14:23:19.555;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@70dea4e
`---[0.251862ms] demo.MathGame:run()
+---[31.65% 0.079715ms ] demo.MathGame:primeFactors() #24
`---[32.17% 0.081018ms ] demo.MathGame:print() #25
第一行:ts=2025-10-20 14:23:19:方法执行时间。
thread_name: 线程名。
id:线程id,此处线程id应该是Arthas内部分配的线程id,与Linux实际线程id不同
。is_daemon: 是否为守护线程。
priority:线程优先级。
TCCL=sun.misc.Launcher$AppClassLoader@70dea4e: 方法的类加载器。
第二行:[0.251862ms] demo.MathGame:run(): run方法执行总耗时。
第三行、第四行:第三第四行层级明显在run方法层级内,而且三四行为同一层级,说明run方法内的有primeFactors方法和print方法,并分别打印了两个方法的耗时。
https://arthas.aliyun.com/doc/trace.htmltt -t demo.MathGame run 命令执行后,会持续记录方法调用情况并在控制台打印记录结果,停止记录方法执行可以按Ctrl+C,如果不手动停止,默认会记录100次自动停止,因为记录需要保存会消耗内存,所以限制了记录次数,而且我们还需定期清理之前的方法调用记录,防止占用太多内存。
INDEX:索引,每一次方法调用的索引,可以理解为id,从1000开始记录。
TIMESTAMP: 方法执行时间。
COST(ms): 方法执行耗时,单位毫秒。
IS-RET: 是否正常返回。
IS-EXP:是否抛出异常
OBJECT:代表MathGame对象
hashcode(), 注意这里不是对象地址。
CLASS:所在类。METHOD:所在方法。
查询所有方法调用记录:
tt -l
查询某个索引对应的方法调用记录,例如查询索引1000的详细方法调用信息:
tt -i 1000
返回值就不解释了,跟之前的解释类似。
重放某个索引对应的方法记录。
tt -p -i 1000-p: 重放方法执行。

查看日志发现方法已经被重新调用了,调用结果如上图。
清除指定索引对应的记录:
tt -d -i 1000清除全部索引记录:
tt --delete-allheapdump /lyc/dump.hprof # 转储此时jvm堆全量内存信息到dump.hprof文件中此文件可以用MAT(内存分析工具)工具打开,可用于分析内存泄漏等问题。MAT下载地址:
https://eclipse.dev/mat/download/
https://arthas.aliyun.com/如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!