開發與維運

Java 虛擬機診斷利器

作者 | 小白一隻

【Arthas 官方社區正在舉行徵文活動,參加即有獎品拿~點擊投稿

1.png

背景

最近學習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 次,然後看執行結果:

2.png

可以看到在 15 次調用 foo 方法後,第 16 次調用 foo 方法是走的 GeneratedMethodAccessor1 來調用的。我嘞個擦,怎麼回事,調著調著就不一樣了,於是跟代碼,跟到了下面這個類:

3.png

其中這句代碼就是對反射調用的次數做了控制


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(阿爾薩斯)了。

4.png

首先下載 arthas:

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

然後啟動 arthas:

java -jar arthas-boot.jar

啟動之後界面長這個樣子:

5.png

其中什麼 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 界面:

6.png

可以看到 32480 正是我們運行的程序,輸入編號 2 去連接到該進程:

7.png

然後就可以將動態生成的類 dump 下來:

dump sun.reflect.GeneratedMethodAccessor1

8.png

可以看到字節碼被 dump 下來了,找到該文件用 javap 來查看:

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

9.png

沒有問題,可以查看到,然後剩下的就是人肉翻譯字節碼啦。。。

本篇關於Arthas的使用其實很少,我只是因為學到這個地方簡單的用了下,但是已經感受到了 Arthas 的強大之處,它甚至還支持 web 界面。。。

10.png

相當厲害!

Arthas 徵文活動火熱進行中

Arthas 官方正在舉行徵文活動,如果你有:

  • 使用 Arthas 排查過的問題
  • 對 Arthas 進行源碼解讀
  • 對 Arthas 提出建議
  • 不限,其它與 Arthas 有關的內容

歡迎參加徵文活動,還有獎品拿哦~點擊投稿

阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的公眾號。”

Leave a Reply

Your email address will not be published. Required fields are marked *