栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java反射机制探秘

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java反射机制探秘

文章目录
  • 概述
  • 本质其实就是根据 符号 拿到符号表以及直接地址 进行调用。
  • 反射调用的实现原理
  • 反射调用的开销
  • 参考

概述

赋予了我们在运行时分析类以及执行类中方法的能力。通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性。

反射

本质其实就是根据 符号 拿到符号表以及直接地址 进行调用。 反射调用的实现原理
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 接口提供了两种实现,一种是本地方法实现,另一种是 Java 实现

那么如何拿到执行地址呐?有两种方式:

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

刚开始生成字节码比较耗时,但是随着 JIT 的即时编译,会越来越快

反射调用的开销
  1. 变长参数方法导致的 Object 数组
    • 由于 Method.invoke 是一个变长参数方法,在字节码层面它的最后一个参数会是 Object 数组(。Java 编译器会在方法调用处生成一个长度为传入参数数量的 Object 数组,并将传入参数一一存储进该数组中
  2. 基本类型的自动装箱、拆箱
    • 由于 Object 数组不能存储基本类型,Java 编译器会对传入的基本类型参数进行自动装箱
  3. 最重要的方法内联
    • 由于 Java 虚拟机的关于上述调用点的类型 profile(注:对于 invokevirtual 或者 invokeinterface,Java 虚拟机会记录下调用者的具体类型,我们称之为类型 profile)无法同时记录这么多个类,因此可能造成所测试的反射调用没有被内联的情况

Method.invoke一直会被内联,但是它里面的 MethodAccesor.invoke 则不一定。
实际上,在C2编译之前循环代码已经运行过非常多次,也就是说MethodAccesor.invoke已经看到多次调用至target()的动态实现。在profile里会显示为有target1,有target2,但是profile不完整,即还有一大部分的调用者类型没有记录。
这时候C2会选择不inline这个MethodAccesor.invoke调用,直接做虚调用。

参考

JVM系列之:JVM是如何实现反射的

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/851675.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号