In-depth analysis of the implementation principle of Java method reflection

Method reflection example

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");
        }
    }
}

Through Java's reflection mechanism, any method of an object can be called during runtime. If a lot of calls are made in this way, will there be performance or memory hidden dangers? In order to thoroughly understand the reflection mechanism of methods, we can only start from the underlying code.

MethodGet

The calling Classclass getDeclaredMethodcan get the method object of the specified method name and parameters Method.

getDeclaredMethod


The method obtains the list of methods declared in privateGetDeclaredMethodsthis from the cache or JVM, and the method will find a method object with matching name and parameters from the returned method list.ClasssearchMethods

searchMethods


If a match is found Method, copy a copy and return, that is, the Method.copy()method


getDeclaredMethodThe object returned each time the method is called Methodis actually a new object, and the rootproperties of the new object all point to the original Methodobject. If you need to call it frequently, it is best to Methodcache the object.

privateGetDeclaredMethods

ClassGet the list of methods declared in this from the cache or JVM. The implementation is as follows:


The reflectionData()method is implemented as follows:


There is a more important data structure here ReflectionData, which is used to cache the following attribute data of the class read from the JVM:


It can be seen from the reflectionData()method implementation: the reflectionDataobject is of a SoftReferencetype, indicating that it may be recycled when the memory is tight, but -XX:SoftRefLRUPolicyMSPerMBthe timing of recycling can also be controlled by parameters. As long as GC occurs, it will be recycled. If it reflectionDatais recycled, the reflection is executed again. method, you can only newReflectionDatarecreate such an object through the method, and the newReflectionDatamethod is implemented as follows:


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


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

资料来源:http://www.jianshu.com/p/3ea4a6b57f87

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324105517&siteId=291194637