反射调用的实现

本文深入探讨了Java反射机制,详细解释了Method.invoke方法的工作原理,包括如何通过委派到MethodAccessor实现本地调用和动态字节码生成。当反射调用次数达到一定阈值时,Java虚拟机会动态生成字节码以提高效率,这一过程称为Inflation。文章揭示了Java反射调用在性能优化方面的策略。

反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。

Java的反射机制,操作的就是这个.class文件,首先加载相应类的字节码(运行eclipse的时候,.class文件的字节码会加载到内存中),随后解剖(反射 reflect)出字节码中的构造函数、方法以及变量(字段),或者说是取出。

方法的反射调用,也就是 Method.invoke,实现如下:

public class Test {

  public static void target(int i) {

    new Exception("#" + i).printStackTrace();

  }



  public static void main(String[] args) throws Exception {

    Class<?> klass = Class.forName("Test");

    Method method = klass.getMethod("target", int.class);

    method.invoke(null, 0);

  }

}

 

public final class Method extends Executable {

  ...

  public Object invoke(Object obj, Object... args) throws ... {

    ... // 权限检查

    MethodAccessor ma = methodAccessor;

    if (ma == null) {

      ma = acquireMethodAccessor();

    }

    return ma.invoke(obj, args);

  }

}

它实际上委派给 MethodAccessor 来处理。MethodAccessor 是一个接口,它有两个已有的具体实现:一个通过本地方法来实现反射调用,另一个则使用了委派模式。

每个 Method 实例的第一次反射调用都会生成一个委派实现,它所委派的具体实现便是一个本地实现。本地实现非常容易理解。当进入了 Java 虚拟机内部之后,我们便拥有了 Method 实例所指向方法的具体地址。这时候,反射调用无非就是将传入的参数准备好,然后调用进入目标方法。

如上面例子,我们获取了一个指向 Test.target 方法的 Method 对象,并且用它来进行反射调用。

可以看到,反射调用先是调用了 Method.invoke,然后进入委派实现(DelegatingMethodAccessorImpl),再然后进入本地实现(NativeMethodAccessorImpl),最后到达目标方法。

采用委派作为一个中间层的意义在于,Java 的反射调用机制还设立了另一种动态生成字节码的实现(下称动态实现),直接使用 invoke 指令来调用目标方法。动态实现和本地实现相比,其运行效率要快上 20 倍。这是因为动态实现无需经过 Java 到 C++ 再到 Java 的切换,但由于生成字节码十分耗时,仅调用一次的话,反而是本地实现要快上 3 到 4 倍。

考虑到许多反射调用仅会执行一次,Java 虚拟机设置了一个阈值 15(可以通过 -Dsun.reflect.inflationThreshold= 来调整),当某个反射调用的调用次数在 15 之下时,采用本地实现;当达到 15 时,便开始动态生成字节码,并将委派实现的委派对象切换至动态实现,这个过程我们称之为 Inflation。

从第15次调用反射时,就触发了动态实现。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值