zoukankan      html  css  js  c++  java
  • JVM如何实现反射

      反射是框架设计的重要手段之一,如Spring的Ioc就是通过反射创建bean实例对象的。虽然反射很常见,但是它的性能损耗也是很昂贵的,甚至是甲骨文关于反射的教学网页,也强调了反射性能开销大的缺点。

    一. 反射方法的调用

      首先我们来看看反射方法的调用,也就是Method.invoke()。

    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);
      }
    }

      跟源码看到,实际上Method是委托给MethodAccessor实现的。MethodAccessor 是一个接口,它有两个已有的具体实现:一个通过本地方法来实现反射调用,另一个则使用了委派模式。为了方便记忆,我便用“本地实现”和“委派实现”来指代这两者。

      每个Method实例第一次调用都会生成一个委托实现,它所委派的具体实现便是一个本地实现。本地实现很好理解,当我们进入虚拟机后,就能拿到class对象或者class实例对象所属方法的内存地址,再通过c++,将传入的参数准备好,直接调用这个方法即可。

      你可能会问,为什么不直接用本地实现,而要通过一个委托实现呢?这是因为除了本地实现,Java 的反射调用机制还设立了另一种动态生成字节码的实现(下称动态实现),直接使用 invoke 指令来调用目标方法。之所以采用委派实现,便是为了能够在本地实现以及动态实现中切换。

      动态实现的速度比本地实现快20倍,这是因为它不用在java和c++质检来回切换。但是动态实现生成字节码十分耗时,仅调用一次的话,反而是本地实现要快上 3 到 4 倍。到这里想一想,如果是你怎么使用这两种方式呢?相信你已经想到了,设置一个阈值判断。实际行虚拟机也是这么做的,这个值被设置为了15,当某个反射调用的调用次数在 15 之下时,采用本地实现;当达到 15 时,便开始动态生成字节码,并将委派实现的委派对象切换至动态实现,这个过程我们称之为 Inflation。

    二. 反射的开销

      方法的反射调用会带来不少性能开销,原因主要有三个:变长参数方法导致的 Object 数组,基本类型的自动装箱、拆箱,还有最重要的方法内联。

  • 相关阅读:
    Handle/Body pattern(Wrapper pattern)
    Python: PS 滤镜--万花筒效果
    Java 工程与 Eclipse 高级用法
    更新服务
    Diskpart挂载/卸载VHD
    Ping批量函数
    Sysprep命令详解
    Hash Table构建
    Invoke-Express 执行多个批处理命令的函数
    磁盘扩容
  • 原文地址:https://www.cnblogs.com/LTEF/p/13674765.html
Copyright © 2011-2022 走看看