深刻分析 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類加載器對象

這裏每次都生成新的類加載器,是爲了性能考慮,在某些狀況下能夠卸載這些生成的類,由於類的卸載是隻有在類加載器能夠被回收的狀況下才會被回收的,若是用了原來的類加載器,那可能致使這些新建立的類一直沒法被卸載,從其設計來看自己就不但願這些類一直存在內存裏的,在須要的時候有就好了。

相關文章
相關標籤/搜索