大家似乎有個(gè)默認(rèn)的常識(shí):java的反射性能不好僧叉,應(yīng)該使用asm奕枝、cglib之類的庫替代。
看java的反射實(shí)現(xiàn)會(huì)有兩種情況瓶堕,參考NativeMethodAccessorImpl
1.調(diào)用native方法隘道,涉及到方法查找,
2.生成字節(jié)碼,動(dòng)態(tài)加載類谭梗,使用MethodAccessorGenerator
就是說在調(diào)用次數(shù)超過閾值15之后忘晤,就會(huì)動(dòng)態(tài)生成類,來執(zhí)行反射方法默辨,本質(zhì)上和直接調(diào)用時(shí)一樣的德频。動(dòng)態(tài)生成類的模式和cglib也是一樣的苍息。
跑了幾個(gè)測(cè)試看看缩幸,計(jì)算的平均每次用時(shí),用的mac jdk8竞思,jvm參數(shù):-server -Xmx4g -Xms4g -Xmn512m? -Xss256k -verbose:gc (原來想貼一下代碼的表谊,簡書的“引用”實(shí)在太難用了)?
循環(huán)次數(shù) ? ?1000 ? ?10*1000 ? 100*1000 ? ? 1000*1000
cglib? ? ? 1392ns? ? 984ns? ? ? 228ns? ? ? ? ? ? ? 78ns
反射? ? ? ? 4753ns? 980ns? ? ? ? 163ns? ? ? ? ? ? ? 36ns
從測(cè)試結(jié)果看出來,次數(shù)越多盖喷,反射的性能越好爆办,這是為什么呢?和jit編譯有關(guān)课梳,使用-XX:+PrintCompilation可以看到距辆,循環(huán)次數(shù)到達(dá)1000*1000的時(shí)候,不僅編譯層數(shù)達(dá)到了4暮刃,而且發(fā)生了OSR編譯(棧上替換)跨算,運(yùn)行在OSR代碼中的次數(shù)越多,性能越好椭懊。
同樣的cglib的FastClass也會(huì)發(fā)生jit編譯诸蚕,也會(huì)使用OSR編譯,但是性能比不過反射氧猬,同時(shí)通過監(jiān)控內(nèi)存背犯,發(fā)現(xiàn)cglib使用的內(nèi)存也比較多,主要是動(dòng)態(tài)生成類的代碼不一樣盅抚。
cglib的FastClass是針對(duì)一個(gè)類動(dòng)態(tài)生成一個(gè)新的類漠魏;反射是針對(duì)每一個(gè)方法生成一個(gè)新的類,方法體比較小妄均,更適合jit做動(dòng)態(tài)編譯柱锹、內(nèi)聯(lián)。
加上-XX:+PrintInlining 參數(shù)丛晦,看內(nèi)聯(lián)的情況奕纫,反射的方法都經(jīng)過了內(nèi)聯(lián)優(yōu)化,而使用cglib生成的代碼無法內(nèi)聯(lián)烫沙,容易出現(xiàn)hot method too big的情況匹层。
查看cglib代碼可以通過System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"./");將動(dòng)態(tài)生成的代碼保存到文件。