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

方法反射实例缓存


    public class ReflectCase {数据结构


        public static void main(String[] args) throws Exception {性能

            Proxy target = new Proxy();ui

            Method method = Proxy.class.getDeclaredMethod("run");设计

            method.invoke(target);代理

        }对象


        static class Proxy {接口

            public void run() {内存

                System.out.println("run");get

            }

        }

    }


经过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类加载器对象

这里每次都生成新的类加载器,是为了性能考虑,在某些状况下能够卸载这些生成的类,由于类的卸载是只有在类加载器能够被回收的状况下才会被回收的,若是用了原来的类加载器,那可能致使这些新建立的类一直没法被卸载,从其设计来看自己就不但愿这些类一直存在内存里的,在须要的时候有就好了。

相关文章
相关标签/搜索