1. 程式人生 > >深入分析Java方法反射的實現原理

深入分析Java方法反射的實現原理

前段時間看了笨神的 從一起GC血案談到反射原理一本,就把Java方法的反射機制實現擼了一遍。

方法反射例項

1

2

3

4

5

6

7

8

9

10

11

12

13

14

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對這兩個欄位重新設定,而且只會設定一次;

如果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類載入器物件

這裡每次都生成新的類載入器,是為了效能考慮,在某些情況下可以解除安裝這些生成的類,因為類的解除安裝是隻有在類載入器可以被回收的情況下才會被回收的,如果用了原來的類載入器,那可能導致這些新建立的類一直無法被解除安裝,從其設計來看本身就不希望這些類一直存在記憶體裡的,在需要的時候有就行了。