美文网首页编程IT梦Java进阶
深入分析Java方法反射的实现原理

深入分析Java方法反射的实现原理

作者: 美团Java | 来源:发表于2017-02-20 20:58 被阅读21361次

简书 占小狼
转载请注明原创出处,谢谢!

“物有本末,事有始终。知其先后,则近道矣”

前段时间看了笨神的 从一起GC血案谈到反射原理一本,就把Java方法的反射机制实现撸了一遍。

方法反射实例

public class ReflectCase {

    public static void main(String[] args) throws Exception {
        Proxy target = new Proxy();
        Method method = Proxy.class.getDeclaredMethod("run");
        method.invoke(target);
    }

    static class Proxy {
        public void run() {
            System.out.println("run");
        }
    }
}

通过Java的反射机制,可以在运行期间调用对象的任何方法,如果大量使用这种方式进行调用,会有性能或内存隐患么?为了彻底了解方法的反射机制,只能从底层代码入手了。

Method获取

调用Class类的getDeclaredMethod可以获取指定方法名和参数的方法对象Method

getDeclaredMethod

其中privateGetDeclaredMethods方法从缓存或JVM中获取该Class中申明的方法列表,searchMethods方法将从返回的方法列表里找到一个匹配名称和参数的方法对象。

searchMethods

如果找到一个匹配的Method,则重新copy一份返回,即Method.copy()方法

所次每次调用getDeclaredMethod方法返回的Method对象其实都是一个新的对象,且新对象的root属性都指向原来的Method对象,如果需要频繁调用,最好把Method对象缓存起来。

privateGetDeclaredMethods

从缓存或JVM中获取该Class中申明的方法列表,实现如下:

其中reflectionData()方法实现如下:

这里有个比较重要的数据结构ReflectionData,用来缓存从JVM中读取类的如下属性数据:

reflectionData()方法实现可以看出:reflectionData对象是SoftReference类型的,说明在内存紧张时可能会被回收,不过也可以通过-XX:SoftRefLRUPolicyMSPerMB参数控制回收的时机,只要发生GC就会将其回收,如果reflectionData被回收之后,又执行了反射方法,那只能通过newReflectionData方法重新创建一个这样的对象了,newReflectionData方法实现如下:

通过unsafe.compareAndSwapObject方法重新设置reflectionData字段;

privateGetDeclaredMethods方法中,如果通过reflectionData()获得的ReflectionData对象不为空,则尝试从ReflectionData对象中获取declaredMethods属性,如果是第一次,或则被GC回收之后,重新初始化后的类属性为空,则需要重新到JVM中获取一次,并赋值给ReflectionData,下次调用就可以使用缓存数据了。

Method调用

获取到指定的方法对象Method之后,就可以调用它的invoke方法了,invoke实现如下:

应该注意到:这里的MethodAccessor对象是invoke方法实现的关键,一开始methodAccessor为空,需要调用acquireMethodAccessor生成一个新的MethodAccessor对象,MethodAccessor本身就是一个接口,实现如下:

acquireMethodAccessor方法中,会通过ReflectionFactory类的newMethodAccessor创建一个实现了MethodAccessor接口的对象,实现如下:

ReflectionFactory类中,有2个重要的字段:noInflation(默认false)和inflationThreshold(默认15),在checkInitted方法中可以通过-Dsun.reflect.inflationThreshold=xxx-Dsun.reflect.noInflation=true对这两个字段重新设置,而且只会设置一次;

如果noInflationfalse,方法newMethodAccessor都会返回DelegatingMethodAccessorImpl对象,DelegatingMethodAccessorImpl的类实现

其实,DelegatingMethodAccessorImpl对象就是一个代理对象,负责调用被代理对象delegateinvoke方法,其中delegate参数目前是NativeMethodAccessorImpl对象,所以最终Methodinvoke方法调用的是NativeMethodAccessorImpl对象invoke方法,实现如下:

这里用到了ReflectionFactory类中的inflationThreshold,当delegate调用了15次invoke方法之后,如果继续调用就通过MethodAccessorGenerator类的generateMethod方法生成MethodAccessorImpl对象,并设置为delegate对象,这样下次执行Method.invoke时,就调用新建的MethodAccessor对象的invoke()方法了。

这里需要注意的是:
generateMethod方法在生成MethodAccessorImpl对象时,会在内存中生成对应的字节码,并调用ClassDefiner.defineClass创建对应的class对象,实现如下:

ClassDefiner.defineClass方法实现中,每被调用一次都会生成一个DelegatingClassLoader类加载器对象

这里每次都生成新的类加载器,是为了性能考虑,在某些情况下可以卸载这些生成的类,因为类的卸载是只有在类加载器可以被回收的情况下才会被回收的,如果用了原来的类加载器,那可能导致这些新创建的类一直无法被卸载,从其设计来看本身就不希望这些类一直存在内存里的,在需要的时候有就行了。


我是占小狼,如果读完觉得有收获的话,欢迎点赞加关注

相关文章

  • java引用疑问

    java引用疑问 java 引用:深入分析Object.finalize方法的实现原理 - 简书Java软引用究竟...

  • java反射原理

    涉及 类加载机制RefectionDataReflectionFactory参考:java反射原理 深入分析Jav...

  • 深入分析 Java 方法反射的实现原理

    前段时间看了笨神的 从一起GC血案谈到反射原理一本,就把Java方法的反射机制实现撸了一遍。 一起GC血案谈到反射...

  • 深入分析Java方法反射的实现原理

    简书 占小狼转载请注明原创出处,谢谢! “物有本末,事有始终。知其先后,则近道矣” 前段时间看了笨神的 从一起GC...

  • Android周报第十一期

    1)深入分析Java ClassLoader原理 2)手机端实现Protocol Buffer protocol ...

  • 死磕系列

    内存模型 【死磕Java并发】-----深入分析synchronized的实现原理 【Java并发编程实战】—–s...

  • Java 源码研究之线程池

    本文是在观看 深入分析java线程池的实现原理 后,对其中讲述的方法虽然了解其功能及大致步骤,但是对其中具体实现依...

  • 深入理解Java虚拟机三

    一、Java反射的实现原理 1.反射调用的实现 反射是Java语言中一个相当重要的特性,它允许正在运行的Ja...

  • 【死磕Java并发】-----Java内存模型之分析volati

    前篇博客【死磕Java并发】-----深入分析volatile的实现原理 中已经阐述了volatile的特性了: ...

  • Java反射实现原理

    Java反射应用十分广泛,例如spring的核心功能控制反转IOC就是通过反射来实现的,本文主要研究一下发射方法调...

网友评论

  • 程序熊大:笨神那篇文章确实给力,狼哥这篇更专注于反射的原理,赞
    美团Java:@杜琪 哈哈,你在笨神熏陶下,感觉要越来越牛逼了
  • 手打丸子:借宝地发个招聘广告:美团目前大量招聘高级java开发工程师,本人团队主own在上海;欢迎各位技术牛人发送简历,简书私信联系; 另外:北京、上海、成都、厦门、深圳各有研发岗位,欢迎联系。
    美团Java:@手打丸子 什么团队?
  • 冉桓彬:你这个源码是jdk什么版本啊? 我这个jdk1.8里面方法的逻辑跟你的不一样;
    美团Java:@冉桓彬 不是7就是8的
  • 冉桓彬:唉, 梦想今年进大厂, 现在一直处于学习中, 看到反射这里了, 然后网上搜文章, 标题铺天盖地的反射机制, 反射原理, 进去一看都是翻译两个API, 写两个demo就完事儿了, 太浪费时间了; 无意间发现一篇文章, 结尾处表明转载你的文章, 如获至宝, 狼哥, 以后有啥不懂的先问你了;
    美团Java:@冉桓彬 哈哈哈哈,没问题
  • e0f91b0029a2:~只是分析了过程,是不是忘记总结呢
  • Ludwigvan::cry: 有点吃力看的
  • honglei92:method 获取123;method 调用123
  • 齐德隆咚强:狼哥,可以加一下微信呗😊
    美团Java:@齐德隆咚强 whywhy23
  • 015a9776a1e7:先看了笨神,再来看了一遍你的。 :smiley:
    美团Java:@不怕死的蚊子 哈哈,没有笨神写的好
  • 齐德隆咚强:不错!技术文章写的很给力!拜读👍
  • 543af3a3f5c3:学习了
  • 手打丸子:冒昧问一下你做了多少年java了,看你文章收获颇多
    _孑孓_:@占小狼 加班时间不好算~~
    美团Java:@手打丸子 这个比较难算
  • StormMa:大神很厉害,学java一年了,做了几个项目,现在回来从源码看起,想大神学习。
  • 4cea09760bc1:写的很棒
    美团Java:@郭起轩 多谢
  • 4e34bdcfcb25:为什么设计不希望这些类一直在内存里
    美团Java:@discoverX 因为占内存
  • 81bade7def3b:用的什么软件
    美团Java:@不利索的阿瓜lord 多谢捧场了啊
    5b30374b7bef:小狼的底子很好,分析的很棒,经常看你的文章!
    美团Java:什么

本文标题:深入分析Java方法反射的实现原理

本文链接:https://www.haomeiwen.com/subject/pxfewttx.html