文档章节

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

Star永恒
 Star永恒
发布于 2017/04/04 16:00
字数 1071
阅读 67
收藏 0

精选30+云产品,助力企业轻松上云!>>>

方法反射实例


    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对这两个字段重新设置,而且只会设置一次;


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

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

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


这里需要注意的是:


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

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

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

Star永恒
粉丝 10
博文 190
码字总数 257696
作品 0
大兴
后端工程师
私信 提问
加载中
请先登录后再评论。
深入理解Constructor之newInstance方法

知其然,知其所以然 0. 前言 在上一篇《反射从入门到精通之深入了解Class类》,我们深入分析了一下 Class 类的原理。在本篇文章,我们分析一下 Constructor 使用方法的原理。 1. Constructor...

osc_qr98xq3d
04/16
3
0
java相关内容

JVM源码分析之JVM启动流程 JVM源码分析之堆内存的初始化 JVM源码分析之Java类的加载过程 JVM源码分析之Java对象的创建过程 JVM源码分析之如何触发并执行GC线程 JVM源码分析之垃圾收集的执行过...

courtzjl
2017/09/01
0
0
Java相关章节知识

##Java 基础## 32位、64位与Java开发研究分析 Java环境变量设置与命令行使用 类构造器继承 对象排序详解 String类详解 Java程序与WEB应用文件路径获取 为什么匿名类中使用局部变量时要声明为...

浮躁的码农
2017/11/01
16
0
视频专辑:张孝祥Java高新技术

专辑:张孝祥Java高新技术 简介:张孝祥java高新技术,非常好的java高级视频教程,内含java的一些新特性,讲的非常详细,适合有java基础者看.

小白forever
2013/11/17
91
0
专辑:Java高新技术(适合已经入门javase,想深入学习者)

简介:市面上目前流传的java基础视频教程都是讲一些最基础的java语法和相关API的应用,然而用人单位对初级程序员的要求越来越高,那些讲解java基础语法的视频教程已经无法满足大众的学习要求。...

crossbell
2014/06/13
33
0

没有更多内容

加载失败,请刷新页面

加载更多

1Mn18Cr18N无磁护环强度高

1Mn18Cr18N高氮奥氏体不锈钢具有强韧性好、耐蚀性好、无磁等诸多优点,成为核主泵飞轮保持环材料的选择。 保持环的热套工序要求保持环材料具备优良的热膨胀性能,飞轮保持环完整性的保证要求...

无磁钢
23分钟前
10
0
比较器,Comparator与Comparable

Comparable比较器 从JDK1.2后提供了比较器的接口:Comparable接口。 public interface Comparable<T>{ /** * 实现对象的比较处理操作 * @param o 要比较的对象 * @return...

哼着我的小调调
35分钟前
9
0
以每种语言编译时,在C和C ++中都有效的代码能否产生不同的行为?

问题: C and C++ have many differences, and not all valid C code is valid C++ code. C和C ++有很多区别,并非所有有效的C代码都是有效的C ++代码。 (By "valid" I mean standard code w......

富含淀粉
40分钟前
7
0
使用getApplication()作为上下文的对话框抛出“无法添加窗口-令牌null不适用于应用程序”

问题: My Activity is trying to create an AlertDialog which requires a Context as a parameter. 我的活动试图创建一个AlertContext,它需要一个Context作为参数。 This works as expect......

法国红酒甜
今天
13
0
亚马逊测评买家号多开_可以解决这个问题嘛?_微信公众号: VMlogin中文版

对于很多亚马逊卖家来说,做亚马逊测评是并不可少的,都在为了自己的店铺能够获得更多的销售,着重培养自己产品的各项属性,以求获得一个更好的权重排名从而获得更多的曝光,但是在旺季期间亚...

竹节猫-ASOer
今天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部