我是靠谱客的博主 冷傲金毛,最近开发中收集的这篇文章主要介绍Java反射原理分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

接下来咱们从源码角度来探索Java反射实现的原理。

public class ReflectionTest {
​
    private static int count=0;
    public static void foo(){
        new Exception((++count)+"次").printStackTrace();
    }
​
    public static void main(String[] args) throws Exception {
        Class<?> clz= Class.forName("ReflectionTest");
        Method method=clz.getMethod("foo");
        for(int i=0;i<20;i++){
            method.invoke(null);
        }
    }
}

对于下面的堆栈执行结果我们可以看到从第17次开始,函数调用方式出现了变化,

java.lang.Exception: 16次
    at com.Reflection.foo(Reflection.java:13)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.Reflection.main(Reflection.java:20)
java.lang.Exception: 17次
    at com.Reflection.foo(Reflection.java:13)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.Reflection.main(Reflection.java:20)

invoke源码如下:

@CallerSensitive
public Object invoke(Object obj, Object... args)
    throws IllegalAccessException, IllegalArgumentException,
       InvocationTargetException
{
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    MethodAccessor ma = methodAccessor;             // read volatile
    if (ma == null) {
        ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);
}

通过查看MethodAccessor的invoke函数,我们可以看到MethodAccessor是一个接口,实现代码如下:

public interface MethodAccessor {
    Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException;
}

我们可以看到MethodAccessor的实现类有3个,分别为DelegatingMethodAccessorImpl、MethodAccessorImpl以及NativeMethodAccessorImpl。通过堆栈我们可以看到在前16次调用的实现类NativeMethodAccessorImpl,之后调用的为GeneratedMethodAccessor1,那么接下来就让我们来探索一下NativeMethodAccessorImpl实现类。

class NativeMethodAccessorImpl extends MethodAccessorImpl {
    private final Method method;
    private DelegatingMethodAccessorImpl parent;
    private int numInvocations;
​
    NativeMethodAccessorImpl(Method var1) {
        this.method = var1;
    }
​
    public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
        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);
        }
​
        return invoke0(this.method, var1, var2);
    }
​
    void setParent(DelegatingMethodAccessorImpl var1) {
        this.parent = var1;
    }
​
    private static native Object invoke0(Method var0, Object var1, Object[] var2);
}

我们可以看到在进行方法调用的过程中存在一个条件判断 吧吧

++this.numInvocations > ReflectionFactory.inflationThreshold()

当在这个阈值之内的时候会调用invoke0方法,这是一个native函数,当超过这个阈值之后生成MethodAccessorGenerator对象之后通过ASM生成新的类sun.reflect.GeneratedMethodAccessor1,可以使用arthas工具查看生成的类的字节码,对字节码翻译之后的代码如下:

public class GeneratedMethodAccessor1 extends MethodAccessorImpl{
    @Override
    public Object invoke(Object obj,Object[] args) throws IllegalArgumentException,InvocationTargetException{
        ReflectTest.foo();
        return null;
    }
}

那么问题来了,为什么要设置一个阈值来进行两种方式的调用呢。其实这是基于性能的考虑,JNI native调用的方式要比动态生成类调用的方式慢20倍左右,但是由于第一次字节码生成的过程是比较慢的,如果反射仅调用一次的话,采用生成字节码的方式反而比native调用的方式慢了3-4倍。因此Java引入了inflation机制,当调用次数小于阈值时,采用native方法,没有额外类的生成、校验以及加载的开销;当超过阈值时,会使用ASM生成新类,保证后面的调用比native要快。

参考书籍:

王磊. Offer来了[M].北京:电子工业出版社,2020.

张亚. 深入理解JVM字节码[M].北京:机械工业出版社,2020.

更多信息关注微信公众号: IT技术发展之路

 

 

 

 

最后

以上就是冷傲金毛为你收集整理的Java反射原理分析的全部内容,希望文章能够帮你解决Java反射原理分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(54)

评论列表共有 0 条评论

立即
投稿
返回
顶部