关于后端:java反射

11次阅读

共计 2015 个字符,预计需要花费 6 分钟才能阅读完成。

JAVA 反射机制是在运行状态中,对于任意一个类,都可能晓得这个类的所有属性和办法;对于任意一个对象,都可能调用它的任意办法和属性;

获取 field 过程:

1:校验成员变量是否容许被拜访:容许被拜访,跳转到步骤 2;否则,抛出 SecurityException 异样;2:调用 searchFields 办法获取属性名为 name 的 field;3:如果步骤 2 的 field 为 null,抛出 NoSuchFieldException 异样,否则,在找到指定的 Field 之后,会 copy 一份返回。保障每次返回的对象是不同的。Fields 列表是从缓存或者 JVM 获取到的。用一个 ReflectionData 的 soft 援用当缓存。ReflectionData 次要是缓存了 Field、Method 和 Constructor 等相干数据。最开始缓存为空,就调用 jvm 本地办法获取,而后将查到的信息缓存到 ReflectionData 外面。保护着 ReflectionData 的 soft 援用,所以 fullgc 的时候会被回收。下次用的时候再次生成。

获取 Method 与 field 过程基本一致。

Method 的调用的过程:

1:校验该办法是否容许被拜访,容许被拜访则跳转到步骤 2,否则,抛出 IllegalAccessException 异样;2:获取以后 method 的 MethodAccessor 对象 ma,如 ma 为 null,则调用 acquireMethodAccessor 办法获取;3: 调用 MethodAccessor 对象的 invoke 办法来实现调用,整个 invoke 办法的外围也在这里。

每个 Method 对象的 root 属性都指向同一个 Method root。每次获取的时候都是 copy 一份。
Method 的 MethodAccessor 赋值:首先看 root 对象是否领有,没有就新创一个赋值给 root,而后用这个 MethodAccessor。即所有 Method 对象共用一个 MethodAccessor。

Method.invoke() 实际上并不是本人实现的反射调用逻辑,而是委托给 sun.reflect.MethodAccessor 来解决。Method.invoke 委派给了 MethodAccessor 解决,MethodAccessor 是 DelegatingMethodAccessorImpl。DelegatingMethodAccessorImpl 再一次委派给了 NativeMethodAccessorImpl。默认状况下,会调用本地办法 invoke0,间接在 JVM 中执行办法调用逻辑,但这里会切换到本地办法调用,存在开销;当 method 实例 A 调用次数超过阈值(默认 15,能够指定 VM 参数 -Dsun.reflect.inflationThreshold=15 调整)的时候,会 new 一个新的 MethodAccessorImpl GeneratedMethodAccessorXX。这个 GeneratedMethodAccessorXX 会替换掉 DelegatingMethodAccessorImpl 中的委托实例,实现切换。这个切换的过程俗称 inflation。
留神:这里的 XX 示意一个自增的数字,示意容许零碎中能够有多个 GeneratedMethodAccessor 同时存在。

JVM 的优化:当某个反射调用造成热点(达到足够的调用次数,15 次)后,这些类的字节码就会被长期生成,这就是 inflation。inflation 就是通过生成字节码的形式,将某个办法的反射调用偷换成了某个具体实例对其本身办法的调用,实质就是“反射”切换成“非反射”的过程。大大减少了本地办法调用的频次,进而晋升了调用性能。

反射开销:

1:Class.forName 会尝试先去办法区(1.8 当前就是 Metaspace)中寻找有没有对应的类,如果没有则在 classpath 中找到对应的 class 文件,通过类加载器将其加载到内存里。留神这个办法波及本地办法调用,这里本地办法调用的切换、类的搜寻以及类的加载都存在开销;2:newInstance,开拓内存空间,实例化曾经找到的 Class;3:getDeclaredMethod/getMethod 遍历 Class 的办法,以办法签名匹配出所须要的 Method 实例,也是比拟重的开销所在;尽管 java.lang.Class 外部保护了一套名为 reflectionData 的数据结构,其自身是 SoftReference 的。Class 会将匹配胜利的 method 缓存到这里,以供下次访问命中,这样一来会加重 method 遍历的开销,然而会减少额定的堆空间耗费(以空间置换工夫);4:反射执行调用本地办法效率绝对较低,优化后,等于从新加载了一个类,有着肯定的空间开销。

参考
https://www.zhihu.com/questio…
https://www.jianshu.com/p/2bc…

正文完
 0