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

深入分析 Java 方法反射的实现原理博主说:Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。在本文中,占小狼分析了Java反射机制的实现原理(源码),感兴趣的同学可以通过本文花上几分钟了解了解。正文方法反射实例publicclassReflectCase{pu

大家好,又见面了,我是你们的朋友全栈君。

博主说:Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。在本文中,占小狼分析了 Java 反射机制的实现原理(源码),感兴趣的同学可以通过阅读本文花上几分钟了解了解。

正文

kava

方法反射实例

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

getDeclaredMethod

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

searchMethods

searchMethods

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

methodcopy

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

privateGetDeclaredMethods

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

privateGetDeclaredMethods

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

reflectionData

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

JVM

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

newReflectionData

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

Method 调用

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

invoke

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

MethodAccessor

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

newMethodAccessor

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

DelegatingMethodAccessorImpl

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

NativeMethodAccessorImpl

这里用到了ReflectionFactory类中的inflationThreshold,当delegate调用了15invoke方法之后,如果继续调用就通过MethodAccessorGenerator类的generateMethod方法生成MethodAccessorImpl对象,并设置为delegate对象,这样下次执行Method.invoke时,就调用新建的MethodAccessor对象的invoke()方法了。这里需要注意的是generateMethod方法在生成MethodAccessorImpl对象时,会在内存中生成对应的字节码,并调用ClassDefiner.defineClass创建对应的 Class 对象,实现如下:

return

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

DelegatingClassLoader

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


转载声明:本文转自简书「占小狼」,深入分析Java方法反射的实现原理

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/157080.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • BD和DVD区域划分

    BD和DVD区域划分BD和DVD区域划分BD:A区:只能是在美国、日本以及香港、台湾等东南亚地区正常播放;B区:只能在欧洲和澳洲等国家正常播放;C区:是在中国大陆、俄罗斯和印度三国才能正常播放。DVD:第一区为:美国、加拿大;第二区为:日本、欧洲、埃及、南非、中东;第三区为:中国台湾、中国香港特别行政区、南韩、东南亚;第四区为:澳洲、新西兰、中南美洲、南太平洋岛屿;第五区为:俄罗斯、蒙古、印度

  • pytorch word2vec_word2vec和lstm

    pytorch word2vec_word2vec和lstmword2vec原理(一):CBOW与Skip-Gram模型基础word2vec原理(二):基于HierarchicalSoftmax的模型word2vec原理(三):基于NegativeSampling的模型目录1.词向量基础1.1One-Hot编码(独热编码)1.2分布式表示:词向量/词嵌入1.3词向量可视化1.4用词嵌入做迁移学习2….

  • MyBatis Plus 实现多表分页查询

    MyBatis Plus 实现多表分页查询在MybatisPlus中,虽然IService<T>接口帮我们定义了很多常用的方法,但这些都是T对象有用,如果涉及到多表的查询,还是需要自定义Vo对象和自己编写sql语句,MybatisPlus提供了一个Page对象,查询是需要设置其中的size字段和current字段的值一、分页配置 可以直接使用selectPage这样的分页,但返回的数据确实…

  • 初始化磁盘_c++怎么初始化

    初始化磁盘_c++怎么初始化磁盘在联机后要初始化,同样也有两种方法,一种是调用IOCTL_DISK_CREATE_DISK,还有一种是调用WMI的Initialize方法。1.首先说说简单的WMI的方法:大致思路同博客:C++实现磁盘联机 先获取磁盘的id,然后执行无参数方法Initialize 核心模块代码如下:wchar_tmsftDiskObjectID[256];GetWMIMSFTDiskObjectId(…

  • windows server2003 web服务器搭建

    windows server2003 web服务器搭建在控制面板中点击添加或删除程序,打开如下界面:勾选并打开应用程序服务器-Internet信息服务(IIS)-万维网服务依次点击确定后,开始下载服务,完成后打开IIS管理器然后进行如下设置在主目录下创建一个文本文档,输入内容,并保存为index.htm打开浏览器,输入IP地址,即可看到刚才输入的内容用物理机访问同理…

  • pycharm2021.11.3 激活码【中文破解版】

    (pycharm2021.11.3 激活码)这是一篇idea技术相关文章,由全栈君为大家提供,主要知识点是关于2021JetBrains全家桶永久激活码的内容IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.cn/100143.html1M3Q9SD5XW-eyJsaWN…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号