深入分析 Java 方法反射的实现原理
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
方法反射實(shí)例
??? 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的反射機(jī)制,可以在運(yùn)行期間調(diào)用對(duì)象的任何方法,如果大量使用這種方式進(jìn)行調(diào)用,會(huì)有性能或內(nèi)存隱患么?為了徹底了解方法的反射機(jī)制,只能從底層代碼入手了。
Method獲取
調(diào)用Class類的getDeclaredMethod可以獲取指定方法名和參數(shù)的方法對(duì)象Method。
getDeclaredMethod
其中privateGetDeclaredMethods方法從緩存或JVM中獲取該Class中申明的方法列表,searchMethods方法將從返回的方法列表里找到一個(gè)匹配名稱和參數(shù)的方法對(duì)象。
searchMethods
如果找到一個(gè)匹配的Method,則重新copy一份返回,即Method.copy()方法
所次每次調(diào)用getDeclaredMethod方法返回的Method對(duì)象其實(shí)都是一個(gè)新的對(duì)象,且新對(duì)象的root屬性都指向原來的Method對(duì)象,如果需要頻繁調(diào)用,最好把Method對(duì)象緩存起來。
privateGetDeclaredMethods
從緩存或JVM中獲取該Class中申明的方法列表,實(shí)現(xiàn)如下:
其中reflectionData()方法實(shí)現(xiàn)如下:
這里有個(gè)比較重要的數(shù)據(jù)結(jié)構(gòu)ReflectionData,用來緩存從JVM中讀取類的如下屬性數(shù)據(jù):
從reflectionData()方法實(shí)現(xiàn)可以看出:reflectionData對(duì)象是SoftReference類型的,說明在內(nèi)存緊張時(shí)可能會(huì)被回收,不過也可以通過-XX:SoftRefLRUPolicyMSPerMB參數(shù)控制回收的時(shí)機(jī),只要發(fā)生GC就會(huì)將其回收,如果reflectionData被回收之后,又執(zhí)行了反射方法,那只能通過newReflectionData方法重新創(chuàng)建一個(gè)這樣的對(duì)象了,newReflectionData方法實(shí)現(xiàn)如下:
通過unsafe.compareAndSwapObject方法重新設(shè)置reflectionData字段;
在privateGetDeclaredMethods方法中,如果通過reflectionData()獲得的ReflectionData對(duì)象不為空,則嘗試從ReflectionData對(duì)象中獲取declaredMethods屬性,如果是第一次,或則被GC回收之后,重新初始化后的類屬性為空,則需要重新到JVM中獲取一次,并賦值給ReflectionData,下次調(diào)用就可以使用緩存數(shù)據(jù)了。
Method調(diào)用
獲取到指定的方法對(duì)象Method之后,就可以調(diào)用它的invoke方法了,invoke實(shí)現(xiàn)如下:
應(yīng)該注意到:這里的MethodAccessor對(duì)象是invoke方法實(shí)現(xiàn)的關(guān)鍵,一開始methodAccessor為空,需要調(diào)用acquireMethodAccessor生成一個(gè)新的MethodAccessor對(duì)象,MethodAccessor本身就是一個(gè)接口,實(shí)現(xiàn)如下:
在acquireMethodAccessor方法中,會(huì)通過ReflectionFactory類的newMethodAccessor創(chuàng)建一個(gè)實(shí)現(xiàn)了MethodAccessor接口的對(duì)象,實(shí)現(xiàn)如下:
在ReflectionFactory類中,有2個(gè)重要的字段:noInflation(默認(rèn)false)和inflationThreshold(默認(rèn)15),在checkInitted方法中可以通過-Dsun.reflect.inflationThreshold=xxx和-Dsun.reflect.noInflation=true對(duì)這兩個(gè)字段重新設(shè)置,而且只會(huì)設(shè)置一次;
如果noInflation為false,方法newMethodAccessor都會(huì)返回DelegatingMethodAccessorImpl對(duì)象,DelegatingMethodAccessorImpl的類實(shí)現(xiàn)
其實(shí),DelegatingMethodAccessorImpl對(duì)象就是一個(gè)代理對(duì)象,負(fù)責(zé)調(diào)用被代理對(duì)象delegate的invoke方法,其中delegate參數(shù)目前是NativeMethodAccessorImpl對(duì)象,所以最終Method的invoke方法調(diào)用的是NativeMethodAccessorImpl對(duì)象invoke方法,實(shí)現(xiàn)如下:
這里用到了ReflectionFactory類中的inflationThreshold,當(dāng)delegate調(diào)用了15次invoke方法之后,如果繼續(xù)調(diào)用就通過MethodAccessorGenerator類的generateMethod方法生成MethodAccessorImpl對(duì)象,并設(shè)置為delegate對(duì)象,這樣下次執(zhí)行Method.invoke時(shí),就調(diào)用新建的MethodAccessor對(duì)象的invoke()方法了。
這里需要注意的是:
generateMethod方法在生成MethodAccessorImpl對(duì)象時(shí),會(huì)在內(nèi)存中生成對(duì)應(yīng)的字節(jié)碼,并調(diào)用ClassDefiner.defineClass創(chuàng)建對(duì)應(yīng)的class對(duì)象,實(shí)現(xiàn)如下:
在ClassDefiner.defineClass方法實(shí)現(xiàn)中,每被調(diào)用一次都會(huì)生成一個(gè)DelegatingClassLoader類加載器對(duì)象
這里每次都生成新的類加載器,是為了性能考慮,在某些情況下可以卸載這些生成的類,因?yàn)轭惖男遁d是只有在類加載器可以被回收的情況下才會(huì)被回收的,如果用了原來的類加載器,那可能導(dǎo)致這些新創(chuàng)建的類一直無法被卸載,從其設(shè)計(jì)來看本身就不希望這些類一直存在內(nèi)存里的,在需要的時(shí)候有就行了。
轉(zhuǎn)載于:https://my.oschina.net/u/2441327/blog/872994
總結(jié)
以上是生活随笔為你收集整理的深入分析 Java 方法反射的实现原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: redis 数据结构
- 下一篇: Java中String、StringBu