zoukankan      html  css  js  c++  java
  • 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 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

  • 相关阅读:
    C# 字符串替换第一次或者最后一次出现的匹配项
    C#:SqlHelper
    C#:SqlHelper
    因为数据库正在使用,所以无法获得对数据库的独占访问权还原或删除数据库的解决方法
    因为数据库正在使用,所以无法获得对数据库的独占访问权还原或删除数据库的解决方法
    C# Nginx平滑加权轮询算法
    C# Nginx平滑加权轮询算法
    VBA InStr 函数
    VBA InStr 函数
    Linq 左连接 left join
  • 原文地址:https://www.cnblogs.com/alisystemsoftware/p/13600428.html
Copyright © 2011-2022 走看看