問題: java 反射為什么會慢呢? 應(yīng)該如何解決? 當作面試題來問吧。
其實不僅僅是面試題,在RPC框架中,使用javassist invoke method, 而不是選擇反射。最近開dubbo 的源碼時候,就發(fā)現(xiàn)rpc 調(diào)用直接使用javassit
第一步: java 反射 是什么?
java 反射 可以觀測 java正在運行的程序,甚至修改程序的動態(tài)行為。比如說可以通過class對象獲取所有的method和屬性,可以通過Method.setAccessible 繞過java 的訪問權(quán)限。我們?nèi)粘J褂玫腎DE一部分使用java反射的原理,當在鍵盤敲擊點的時候就可以列出所有類的所有的方法。spring框架IoC也是依賴反射機制。但是反射的運行速度是有人會抱怨的。下面先看看反射的API:
使用java 反射的API 首先
(1)獲取class 對象,可以通過Class.forName, getClass, 或者直接.class 訪問。對于數(shù)組的class 對象, 可以通過Class.getComponentType() 方法獲得數(shù)組元素的類型
(2)使用newInstance 獲取class 對象的instance, 通過getFields,getConstructors, getMethods 獲取該類的成員。method.invoke(object, object[])來調(diào)用方法,field.get/set(object)來訪問字段的值
第二步:java 反射和 JVM
java的反射先調(diào)用了 Method.invoke,然后進入委派實現(xiàn) DelegatingMethodAccessorimpl, 再進入本地實現(xiàn)NativeMethodAccessorImpl。java 反射使用委派機制是為了能夠在本地以及動態(tài)實現(xiàn)中切換。動態(tài)實現(xiàn)就是指動態(tài)生成字節(jié)碼的實現(xiàn)。動態(tài)字節(jié)碼生成的運行效率比反射的高。但是生成字節(jié)碼的過程也是耗時的,因此java 虛擬機通過Dsun.reflect.inflationThreashold = 15, 來決定是采用動態(tài)字節(jié)碼生成還是本地運行。其中JAVAssist 就是可以動態(tài)生成 字節(jié)碼。
那么java 反射為什么會慢呢?
(1)class.getMethod, 會遍歷公有方法,如果匹配不到,還會遍歷父類公有方法,在代碼中,我們最好能夠緩存遍歷出來的結(jié)果
(2) method.invoke 是一個變長的參數(shù)方法,最后一個參數(shù)是Object 數(shù)組,Object 數(shù)組不能存儲基本類型,會對傳入的基本類型參數(shù)自動裝箱。java緩存了[-128, 127]中的所有整數(shù)對應(yīng)的Integer 對象,不再這個范圍內(nèi)的,需要新建一個integer對象
最近看dubbo 源代碼,對其dubbo 的spi 設(shè)計, 和 javassist 使用比較感興趣,原來背后都是有原因。是不是很神奇?