一文带你掌握Java诊断利器Arthas

什么是Arthas?Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异...

什么是Arthas?Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。

Arthas(阿尔萨斯)能为你做什么?

Arthas 是 Alibaba 开源的 Java 诊断工具,深受开发者喜爱。

当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:

  1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  5. 是否有一个全局视角来查看系统的运行状况?
  6. 有什么办法可以监控到 JVM 的实时运行状态?
  7. 怎么快速定位应用的热点,生成火焰图?
  8. 怎样直接从 JVM 内查找某个类的实例?


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.webp

每一行代表一次执行结果打印,比如第一行打印信息提示违法参数数量为1,当前参数为-200534,需要大于2,第三行打印209949分解质因数的结果为3*47*1489.
启动Arthas

curl -O https://arthas.aliyun.com/arthas-boot.jarjava -jar arthas-boot.jar

启动后控制台出现如下打印:

2.webp

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

3.webp

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

帮助命令,输入后会展示arthas所有可用命令以及命令介绍。

4.webp

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

5.webp

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

https://arthas.aliyun.com/doc/commands.html

一些常用命令

6.webp

dashboard

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

7.webp

输出结果分为三部分,第一部分展示线程信息,第二部分展示内存和GC信息,第三部分展示部分JVM运行时变量信息。常用参数:

-i 刷新实时数据的时间间隔 (ms),默认 5000ms-n 刷新实时数据的次数

直接输入【dashboard】命令命令行界面会每隔5秒重新刷新一次采集数据信息,无限循环,可以按ctrl+c退出,输入【dashboard -i 2000 -n 2】效果为刷新实时数据间隔为2s,一共只刷新两次自动结束。thread查看线程相关信息

thread  #显示第一页线程信息

8.webp

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

9.webp

thread id   #显示对应线程id的堆栈信息,id为上面显示的ID列的值。

上面可以查看线程ID为1的线程为main方法所在线程

10.webp


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

11.webp

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

12.webp
注意, 目前只支持找出 synchronized 关键字阻塞住的线程, 如果是java.util.concurrent.Lock, 目前还不支持。

sm(Search Method),搜索jvm已经加载的方法信息

sm -d demo.MathGame   #查看MathGame类的方法详细信息基本语法: 

sm -d 全限定类名  #查看MathGame类的方法详细信息 ,-d代表详细信息

sm -h  #帮助命令,查看更详细用法

13.webp
每一部分展示一个方法的详细信息,返回值解释:

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  #帮助命令,查看更详细用法


14.webp
执行结果解释:

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  #反编译类,这里类使用全限定类名

15.webp
反编译第一部分显示类加载器信息,第二部分显示该类所在jar包位置,第三部分显示反编译的源码,源码部分还会显示对用行号。

如果只想显示源码,可以使用添加 --source-only 参数。

jad demo.Math primeFactors  #反编译math-game中的分解质因数方法。

16.webp
mc 内存编译,类似于jdk中的javac命令,能够把.java文件编译为.class文件。

mc java文件 -d 目录  #将java文件编译成class文件并输出到指定目录

 mc -h  # 查看更详细用法

详细用法稍后结合一个案例一起演示。

redefine  中文翻译重新定义,作用是加载外部.class文件到JVM中,可以实现热更新功能。
redefine class文件  #将class文件加载到JVM内存中redefine -h  # 查看更详细用法
详细用法稍后结合一个案例一起演示。


jad | mc | redefine 三个命令组合使用的话可以实现热编译功能,也就是我们所谓的无需重启JVM就更新代码的功能。首先用jad反编译代码, 加上--source-only仅编译源码
jad demo.MathGame --source-only > /lyc/MathGame.java#只反编译MathGame 类源码并将内容输入到MathGame.java文件中。 

反编译完成后,我们尝试用vim编辑器编辑代码内容。
17.webp

我在run方法中第一行添加了字符串【哎哟,你干嘛啊啊啊啊啊】的打印。wq保存退出。
18.webp

我在run方法中第一行添加了字符串【哎哟,你干嘛啊啊啊啊啊】的打印。wq保存退出。接下来使用mc命令将这个修改后的java文件编译成class文件
mc /lyc/MathGame.java -d /lyc# 将MathGame.java文件编译为class文件输出到lyc目录中

19.webp

最后,使用redefine命令将编译后的class文件重新加载进JVM。
redefine  /lyc/demo/MathGame.class# 将MathGame类加载进JVM内存中
20.webp
此时查看控制台打印,发现热更新成功了。
21.webp

关于redefine命令的限制:
关于redefine命令的限制:
  • redefine 的 class 不能修改、添加、删除类的 field 和 method,包括方法参数、方法名称及返回值

  • 目前 redefine 和 watch/trace/jad/tt 等命令冲突,以后重新实现 redefine 功能会解决此问题

  • 正在跑的函数,没有退出不能生效,比如下面新增加的System.out.println,只有run()函数里的会生效22.webp

jad | mc | retransform 三个命令也可以实现热编译功能retransform 命令跟上面redefine作用类似,也可以加载.class文件到JVM内存,Arthas官网更推荐使用
retransform 。因为用法类似,就不做详细介绍,可以直接参考官网地址:
https://arthas.aliyun.com/doc/retransform.html
watch作用是观察方法的入参、返回值、异常、实例对象本身等信息。基本语法:
watch 全限定类名 方法名 【观察表达式】
比如我想查看primeFactors 分解质因数方法的【请求参数,MathGame对象实例,返回值,异常】四种信息。
watch demo.MathGame primeFactors '{params, target, returnObj, throwExp}'
观察表达式写法其实是使用的ognl表达式,关于ognl表达式的基本用法,可以自行百度。此处{params, target, returnObj, throwExp}就代表观察表达式,代表观察指定方法的【请求参数,MathGame对象实例,返回值,异常】四种信息,  加上 ‘{}’ ,代表这四个对象信息以一个ArrayList对象返回。
23.webp
截图的每一部分框选代表一次方法的执行,
ts=2025-10-20 13:54:08.018; [cost=0.099982ms] result=@ArrayList[
    @Object[][isEmpty=false;size=1],
    @MathGame[demo.MathGame@6193b845],
    null,
    @IllegalArgumentException[java.lang.IllegalArgumentException: number is: -91737, need >= 2],
]

抽取其中一次方法执行进行解释:
ts=2025-10-2013:54:08.018: 方法开始执行时间。

[cost=0.099982ms]: 方法执行耗时。

result:观察结果,它的值类型我们看到为ArrayList,里面有四个属性,
依次对应我们填写的观察表达式【请求参数,MathGame对象实例,返回值,
异常】,【请求参数】是个List对象,大小为1,这里展示的只是其对象,
看不到里面的值,【MathGame对象实例】也是展示的对象hashcode,返
回值为空, 抛出了违法参数异常。

如果我们想要看到【请求参数】或者【MathGame对象实例】更详细的值,而不只是对象hascode,我们可以
24.webp

添加【-x 2】参数后,我们发现【请求参数】对应的值能看见了,可以看到是Integer类型值为1, 【MathGame对象实例】也能看到MathGame对象的random属性,和illegalArgumentCount属性,如果想看到random属性的详细信息可以设置为【-x 3】,【-x】参数可以设置的层级为1到4。该命令执行后默认会一直监控方法执行,可按Ctrl+C退出监控。watch -h 查看可查看更详细的用法。watch官方文档地址

https://arthas.aliyun.com/doc/watch.html

trace作用是监控某个方法的执行耗时,以及方法里面每个子方法的执行耗时,有些情况下,我们需要统计一个方法中哪个部分耗时长,并对该部分长耗时代码进行优化,但是忘记加日志对耗时打印,就可以使用此命令。基本语法:
trace 全限定类名 方法名
我们以监控run方法耗时为例。
trace demo.MathGame run

25.webp
上面截图的每一个框选代表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方法,并分别打印了两个方法的耗时。

默认情况下,trace命令不会打印JDK本身的方法耗时,如果需要打印,可以添加【--skipJDKMethod true】。【trace -h】查看更多详细用法。trace官方文档地址:
https://arthas.aliyun.com/doc/trace.html


tttt(Time Tunnel), 翻译为时间隧道, 可以记录下每次方法调用记录,包括但不限于记录方法调用的参数,返回值,异常信息等,而且支持方法重放,即由于我们保存了方法调用参数,后面可以根据参数再次调用。
开始记录run方法调用:
tt -t demo.MathGame run 
命令执行后,会持续记录方法调用情况并在控制台打印记录结果,停止记录方法执行可以按Ctrl+C,如果不手动停止,默认会记录100次自动停止,因为记录需要保存会消耗内存,所以限制了记录次数,而且我们还需定期清理之前的方法调用记录,防止占用太多内存。
26.webp

表头解释:
INDEX:索引,每一次方法调用的索引,可以理解为id,从1000开始记录。

TIMESTAMP: 方法执行时间。

COST(ms): 方法执行耗时,单位毫秒。

IS-RET: 是否正常返回。

IS-EXP:是否抛出异常

OBJECT:代表MathGame对象
hashcode(), 注意这里不是对象地址。

CLASS:所在类。METHOD:所在方法。

查询所有方法调用记录:

tt -l

27.webp

查询某个索引对应的方法调用记录,例如查询索引1000的详细方法调用信息:

tt -i 1000

28.webp

返回值就不解释了,跟之前的解释类似。

重放某个索引对应的方法记录。

tt -p -i 1000

-p: 重放方法执行。

29.webp

查看日志发现方法已经被重新调用了,调用结果如上图。

清除指定索引对应的记录:

 tt -d -i 1000

清除全部索引记录:

tt --delete-all

heapdumpdump java head,堆转储,类似于jmap的head dump功能。
 heapdump  /lyc/dump.hprof # 转储此时jvm堆全量内存信息到dump.hprof文件中
此文件可以用MAT(内存分析工具)工具打开,可用于分析内存泄漏等问题。MAT下载地址:
https://eclipse.dev/mat/download/

30.webp
注意:heapdump可能会对应用性能产生影响,因此在生产环境中应谨慎操作,最好在低峰时段进行。

总结:今天的介绍旨在让大家认识到Arthas工具的强大,如果能够活用它,也许能够帮助我们快速排查解决许多线上问题。Arthas官方提供的命令一共有45个,我不可能通过一篇文章将所有命令都介绍清楚,我能做的,是让大家认识到Arthas的强大,然后通过这篇文章开始,将Arthas在实战中真正的用起来,更多更详细的用法,请参考Arthas官方文档。



https://arthas.aliyun.com/
  • 发表于 2025-12-02 12:32
  • 阅读 ( 61 )

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
shitian
shitian

662 篇文章

作家榜 »

  1. shitian 662 文章
  2. 石天 437 文章
  3. 每天惠23 33 文章
  4. 小A 29 文章