JAVA-提高反射效率__JAVA
來源:互聯網
上載者:User
JAVA-提高反射效率
功能說明: 從事java開發的都知道反射的運行速度慢,所以很多java的開發人員都對反射機制的使用望而卻步(包括BME組件SDO)。我想知道,究竟反射機制慢在哪裡。有沒有改進方法,讓我們可以繼續使用它。如果一個好東西因為其自身的一些缺陷而不使用它,那麼實在可惜,反射也是這樣。我想說的是:我們應該一點點的改進它。
代碼特點: 1. 描述
從事java開發的都知道反射的運行速度慢,所以很多java的開發人員都對反射機制的使用望而卻步(包括BME組件SDO)。我想知道,究竟反射機制慢在哪裡。有沒有改進方法,讓我們可以繼續使用它。如果一個好東西因為其自身的一些缺陷而不使用它,那麼實在可惜,反射也是這樣。我想說的是:我們應該一點點的改進它。
2. 錯誤的使用方法
錯誤的使用方法是每次需要擷取Class的對象時都使用Class.forName方法,或者需要調用Class對象上的方法時都調用getDeclaredMethod(String name, Class<?>... parameterTypes)或getMethod(String name, Class<?>... parameterTypes)方法擷取Method對象,再調用其上的invoke(Object obj, Object... args)方法。
這裡存在兩個容易造成效能損耗的地方:
Class.forName方法的調用會執行Class類檔案在整個類路徑下的搜尋,頻繁調用比較影響效能。
Class對象上的getDeclaredMethod (String, Class<?>...)或getMethod(String, Class<?>...)方法的調用會執行Method對象在Class對象上的搜尋。有些同事還使用getMethods()方法擷取Method數組,然後執行搜尋任務,實際上getMethods()還會執行方法對象的集體Copy比直接使用(String, Class<?>...)或getMethod(String, Class<?>...)方法還要消耗時間及空間。
3. Cache思想
Cache的思想是將需要的反射中介軟體給儲存下來,以便以後使用。不管使用什麼方法擷取Class對象上的Method對象,返回的都是Method對象的copy對象。這些copy對象有的只是使用一次就被回收了,未免有些可惜。我們可這以將這些對象給緩衝下來,以便以後使用。而在儲存資料結構中,無疑HashMap的尋找速度是最快的,它主要是通過對象的Hash碼進行一次尋找,速度超快。但是HashMap上的操作不是安全執行緒的,需要改進方法實現同步。
4. 具體實現
需要兩個組件ClassInfo和ReflectionCache。
ClassInfo主要儲存Class對象的資訊,主要是方法Map。其中ClassInfo中包括三部分方法的Map: Getter, Setter, Other。Getter是Class的屬性的擷取方法,Setter是Class的屬性的設定方法,Other是其它方法。需要注意的是Getter和Setter的方法需要完全符合Javabean規範(isXXX方法屬於Getter方法範圍內),其key值是方法對應的屬性名稱。Other方法是除Getter和Setter以外的其它方法。
ReflectionCache組件主要是通過HashMap對ClassInfo進行緩衝。緩衝的索引值是ClassInfo中Class對象的全稱。如一個String對象,它緩衝的索引值就是java.lang.String。並且ReflectionCache提供了幾種不同get和put方法來方便使用者的操作。
另外ClassInfo的產生需要用到ClassInfoUtils工具。它的主要工作是建立ClassInfo對象,其中建立ClassInfo時可以提供Method Type資訊來指定緩衝的方法類型(如:所有方法-All、存取器方法-Access、擷取方法-Getter和設定方法-Setter)。
5. 同步控制
ReflectionCache中ClassInfoMap是一個靜變數,那麼隨之而來的就是HashMap的同步問題。我對ReflectionCache的做了些改進,主要是對put方法的處理。首先HashMap的擷取操作(get操作)沒有加入同步操作,因此擷取的操作是可以並發的。現在的問題在於如果擷取不了ClassInfo對象時會要執行設定作業(set操作),此時並發問題隨之而來。可能在同一時刻會有很多線程去設定ClassInfo,在第一個設定完ClassInfo的線程結束後,第二個線程應該停止設定ClassInfo。在此需求之上,我們需要對ReflectionCache的put操作上加上同步塊,並且讓put操作再執行一個額外的操作:返回添加到ClassInfoMap中的ClassInfo,不管它是不是其它線程添加的。因此我們在設定ClassInfo時,可以這樣操作:
ClassInfo classInfo = ReflectionCache.putClassInfo(String.class);
6. 改進效率
改進之後的效率的提高是明顯的。主要是節省了中間變數建立及反射資料的尋找時間。
測試資料
100000次,20個線程,無Class.forName操作
一般用法: 11375 milliseconds
ReflectionCache: 2562 milliseconds
100000次,20個線程,有Class.forName操作
一般用法: 16125 milliseconds
ReflectionCache: 4187 milliseconds
可見使用ReflectionCache明顯提高了效率。
7. 使用方法
首先匯入ReflectionCache類檔案或者將其打成jar包放在類路徑下。
擷取ClassInfo:
ClassInfo classInfo = ReflectionCache.getClassInfo(“java.lang.String”);
設定ClassInfo:
classInfo = ReflectionCache.putClassInfo(String.class);
getClassInfo和putClassInfo方法的有兩種重載函數,分別對應參數是Object和Class的兩種情況。