阿里开源的Java 虚拟机诊断利器

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

_images/arthas.png

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

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

  1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?

  2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?

  3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?

  4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!

  5. 是否有一个全局视角来查看系统的运行状况?

  6. 有什么办法可以监控到JVM的实时运行状态?

  7. 怎么快速定位应用的热点,生成火焰图?

Arthas支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

请查看入门教程

背景

最近学习Java字节码过程中遇到了反射,有段代码是这样的:

package com.example.classstudy;

import java.lang.reflect.Method;

/**
 * @author TY
 */
public class ReflectionTest {

    private static int count = 0;
    public static void foo() {
        new Exception("test#" + (count++)).printStackTrace();
    }

    public static void main(String[] args) throws Exception {
        Class<?> clz = Class.forName("com.example.classstudy.ReflectionTest");
        Method method = clz.getMethod("foo");
        for (int i = 0; i < 20; i++) {
            method.invoke(null);
        }
    }
}

就是一段简单的反射调用 foo 方法,执行 20 次,然后看执行结果:



可以看到在 15 次调用 foo 方法后,第 16 次调用 foo 方法是走的 GeneratedMethodAccessor1 来调用的。我嘞个擦,怎么回事,调着调着就不一样了,于是跟代码,跟到了下面这个类:



其中这句代码就是对反射调用的次数做了控制

if (++this.numInvocations > ReflectionFactory.inflationThreshold() 
&& !ReflectUtil.isVMAnonymousClass(
this.method.getDeclaringClass())) {
            MethodAccessorImpl var3 = (MethodAccessorImpl)
            (new MethodAccessorGenerator())
            .generateMethod(this.method.getDeclaringClass(), 
            this.method.getName(), 
            this.method.getParameterTypes(), 
            this.method.getReturnType(), 
            this.method.getExceptionTypes(), 
            this.method.getModifiers());
            this.parent.setDelegate(var3);
        }

this.numInvocations 的默认值是 0,而 ReflectionFactory.inflationThreshold() 默认是 15,当大于 15 的时候会通过 ASM 技术动态生成 GeneratedMethodAccessor1 类来调用 invoke 方法,但是,因为是动态生成的,我们怎么才能看到这个类实际长什么样子呢?

Arthas

这个时候,就可以用上阿里的 arthas(阿尔萨斯)了。



首先下载 arthas:

curl -O https://alibaba.github.io/arthas/arthas-boot.jar

然后启动 arthas:

java -jar arthas-boot.jar

启动之后界面长这个样子:



其中什么 23012, 28436 等是当前环境中现有的 java 进程,然后需要连接到哪个进程就输前面的编号(1234 啥的),输了之后回车。那么我首先改写一下最开始的那个程序,让他不退出:

package com.example.classstudy;

import java.lang.reflect.Method;

/**
 * @author TY
 */
public class ReflectionTest {

    private static int count = 0;
    public static void foo() {
        new Exception("test#" + (count++)).printStackTrace();
    }

    public static void main(String[] args) throws Exception {
        Class<?> clz = Class.forName("com.example.classstudy.ReflectionTest");
        Method method = clz.getMethod("foo");
        for (int i = 0; i < 20; i++) {
            method.invoke(null);
        }

        System.in.read();
    }
}

重新启动程序之后,查看 arthas 界面:



可以看到 32480 正是我们运行的程序,输入编号 2 去连接到该进程:



然后就可以将动态生成的类 dump 下来:

dump sun.reflect.GeneratedMethodAccessor1



可以看到字节码被 dump 下来了,找到该文件用 javap 来查看:

javap -c -v -p -l GeneratedMethodAccessor1.class



没有问题,可以查看到,然后剩下的就是人肉翻译字节码啦。。。

本篇关于Arthas的使用其实很少,我只是因为学到这个地方简单的用了下,但是已经感受到了 Arthas 的强大之处,它甚至还支持 web 界面。。。



相当厉害!


本文系作者在时代Java发表,未经许可,不得转载。

如有侵权,请联系nowjava@qq.com删除。

编辑于

关注时代Java

关注时代Java