Java 虚拟机诊断利器

作者 | 小白一只

【Arthas 官方社区正在举征文活动,参加即有奖品拿~点击投稿】

Java 虚拟机诊断利器

背景

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

package com.example.classstudy;
import java.lang.reflect.Method;
/**
* @author TY
*/
public claE ) l d %ss Reflecq | B ctionTest {
private static int count = 0;
public static void fk  D h V Z J K Voo() {
new Exception("test#" + (count++)).printStackTrace();
}
public static void main(String[] args) throws Exception {
Class&lt % : u 0 + g z Vt;?> clz = Class.forName("com.example.classstudy.Refz ` ; l YlectionTest");
Method method = clz` _ ; T H.getMethod(A J i 5 ! [ /"foo");
for (int i = 0; i < 20; iR | 3 L t I W++) {
method.invoke($ m 2 :null);o D i c A (
}
}
}

就是一段简单的反射调用 foo 方法,执 20 次,然后看a y ? x * l u执行结果:

Java 虚拟机诊断利器

可以看到在 15h ^ % $ z [ 次调用 foo 方e 2 A n C K 2 . c法后,第 16 次调用 foo 方法是走的 GeneratedMethodA^ Q 1 p 4 }ccessor1 来调用的。我嘞个擦,怎么回事,调着调着就不一样了,于是跟代码,跟到了下面这个类:# c B ;

Java 虚拟机诊断利器

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


if (++this.G ! L @ 9 = h EnumInvocations > ReflectionFacx j j mtory.inflationThreshold()
&& !Ri n , ZeflectUtil.is5 ; g MVMAnonymousClass(
this.method.getDeclaringClass())) {
MethodAccessorImpl var3 = (MethodAccessorImpl)
(new MethodAccessorGenerat- b P B X x L ~or()T { F  d I o)
.generateMethod(this.method.getDeclaringClass(),
tE ; T nhis.method.getName(),
this.method.getParameterTypes(),
this.method.getReturnType(),
this.methoO P P L D ] 9 |d.getExceptionTypes(),
this.method2 4 D.getModifiers());
this.parent.setDelegate(var3);
}

this.numInvocations 的默认值是 0,而I 9 k 5 ^ # ReflectionFactory.inN I vflationThreshold() 默认是 15,当大于 15 的时候会通过 ASM 技术动态生成 GeneratedMetc 9 ) [ Z U .hodAccessor1 类来调用 invoF W ske 方法,但是,因为是动态生成的,我们怎么才能看到这个类实际长什么样子呢?

Arthas

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

Java 虚拟机诊断利器

首先下载 arthas:

curl-Ohttps://alibaba.github.io/arthas/arthas-booO yt.jar

然后启动 arthas:

java-jararthas-b: K z 1 Loot.jar

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

Java 虚拟机诊断利器

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

package com.example.classstudy;N W A ~ ; `
import java.lang.reflect.Methoo O o Ad;
/**
* @author TY
*/
public clasK 0 m 6 U / & Is Reflectionf M vTest {
private static int count = 0;
pt 9 + gublic static void foo() {
nZ ~ c ew Exception("test#" + (count++)).pq N ( = printStackTrace();
}
public static void main(String[] args) throws Exception {
Class<?> clz = Class.forNam3 } r / S ; 2 Se("com.exampY 8 R y { Z d ole.clas4 w ! w k Y N t Qsstudy.ReflectionTest");
Method method = clz.getMethod("foo");
for (int i = 0; i < 20; i++) {
method.invoke(n$ & ~ s 6 1 ( a Yull) p I 3 N ? v;
}
System.in.read();
}
}

重新启g D j / I动程序之后,查看 arthas 界面:

Java 虚拟机诊断利器

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

Java 虚拟机诊断利器

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

dumpsun.reflect.GenerA [ katedMethodAccessor1

Java 虚拟机诊断利器

可以看到字节码被 dumc & *p 下来了,找到该文件用 javap 来查看

javap-c-v-p-lGeneratedMet2 P l b 5 |hodAccessor1.class

Java 虚拟机诊断利器

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

本篇关于Arthas的使用其实很少,我z { } K 3 G J只是因为学到这个地方简单的用了下,但是已o Y R # a . z经感受到了 Arthas 的强大之处@ P n z q c,它甚至还支持 web 界面。。。

Java 虚拟机诊断利器

相当厉害!

Arthas 征文活动火热进行中

Arthas 官方正在举行征文活动,g F ; { 5 ( ~ !如果你有:

  • 使用 Are [ * ? ` F sthas 排查过m @ a的问题
  • 对 Arthas 进行源码解读
  • 对 Arthas 提出建议
  • 不限,其它与 Arthasb . C d ~ 有关的内容

    欢迎参加征文活动,还有奖品拿哦~点击投稿

“阿里巴巴原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦原生流行技术趋势、云原生大规模的落地实践,做最懂云% | p t原生N [ u开发者的公众号。”