• 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 数组,基本类型的自动装箱、拆箱,还有最重要的方法内联。

  • 相关阅读:
    用asp.net还原与恢复sqlserver数据库(转)
    子窗口和父窗口交互
    Oracle 数据库导入导出和windows环境下的oracle服务
    从...中检测到有潜在危险的 Request.Form 值的解决办法 和嵌入页面代码
    ccat – 使用语法突出显示输出内容
    如何在Linux中使用Shell脚本终止用户会话?
    如何在Rescue模式下配置网络和SSH登录
    Linux 是洗衣粉!关于Linux 的10个趣事
    讲述:一个月薪12000的北京程序员的真实生活
    Linux文件的颜色代码
  • 原文地址:https://www.cnblogs.com/LTEF/p/13674765.html
Copyright © 2020-2023  润新知