java 反射的效率_如何提高使用Java反射的效率?
前言
在我們平時的工作或者面試中,都會經(jīng)常遇到“反射”這個知識點,通過“反射”我們可以動態(tài)的獲取到對象的信息以及靈活的調(diào)用對象方法等,但是在使用的同時又伴隨著另一種聲音的出現(xiàn),那就是“反射”很慢,要少用。難道反射真的很慢?那跟我們平時正常創(chuàng)建對象調(diào)用方法比慢多少? 估計很多人都沒去測試過,只是”道聽途說“。下面我們就直接通過一些測試用例來直觀的感受一下”反射“。
正文
準(zhǔn)備測試對象
下面先定義一個測試的類TestUser,只有id跟name屬性,以及它們的getter/setter方法,另外還有一個自定義的sayHi方法。
public classTestUser {privateInteger id;privateString name;publicString sayHi(){return "hi";
}publicInteger getId() {returnid;
}public voidsetId(Integer id) {this.id =id;
}publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}
}
測試創(chuàng)建100萬個對象
//通過普通方式創(chuàng)建TestUser對象
@Testpublic voidtestCommon(){long start =System.currentTimeMillis();
TestUser user= null;int i = 0;while(i<1000000){++i;
user= newTestUser();
}long end =System.currentTimeMillis();
System.out.println("普通對象創(chuàng)建耗時:"+(end - start ) + "ms");
}//普通對象創(chuàng)建耗時:10ms//通過反射方式創(chuàng)建TestUser對象
@Testpublic void testReflexNoCache() throwsException {long start =System.currentTimeMillis();
TestUser user= null;int i = 0;while(i<1000000){++i;
user= (TestUser) Class.forName("ReflexDemo.TestUser").newInstance();
}long end =System.currentTimeMillis();
System.out.println("無緩存反射創(chuàng)建對象耗時:"+(end - start ) + "ms");
}//無緩存反射創(chuàng)建對象耗時:926ms
在上面這兩個測試方法中,筆者各自測了5次,把他們消耗的時間取了一個平均值,在輸出結(jié)果中可以看到一個是10ms,一個是926ms,在創(chuàng)建100W個對象的情況下,反射居然慢了90倍左右。wtf?差距居然這么大?難道反射真的這么慢?下面筆者換一種反射的姿勢,繼續(xù)測試一下,看看結(jié)果如何?
//通過緩存反射方式創(chuàng)建TestUser對象
@Testpublic void testReflexWithCache() throwsException {long start =System.currentTimeMillis();
TestUser user= null;
Class rUserClass= Class.forName("RefleDemo.TestUser");int i = 0;while(i<1000000){++i;
user=(TestUser) rUserClass.newInstance();
}long end =System.currentTimeMillis();
System.out.println("通過緩存反射創(chuàng)建對象耗時:"+(end - start ) + "ms");
}//通過緩存反射創(chuàng)建對象耗時:41ms
咦?這種操作只需要41ms了,大大提高了反射創(chuàng)建對象的效率。為什么會快這么多呢?
其實通過代碼我們可以發(fā)現(xiàn),是Class.forName這個方法比較耗時,它實際上調(diào)用了一個本地方法,通過這個方法來要求JVM查找并加載指定的類。所以我們在項目中使用的時候,可以把Class.forName返回的Class對象緩存起來,下一次使用的時候直接從緩存里面獲取,這樣就極大的提高了獲取Class的效率。同理,在我們獲取Constructor、Method等對象的時候也可以緩存起來使用,避免每次使用時再來耗費時間創(chuàng)建。
測試反射調(diào)用方法
@Testpublic void testReflexMethod() throwsException {long start =System.currentTimeMillis();
Class testUserClass= Class.forName("RefleDemo.TestUser");
TestUser testUser=(TestUser) testUserClass.newInstance();
Method method= testUserClass.getMethod("sayHi");int i = 0;while(i<100000000){++i;
method.invoke(testUser);
}long end =System.currentTimeMillis();
System.out.println("反射調(diào)用方法耗時:"+(end - start ) + "ms");
}//反射調(diào)用方法耗時:330ms
@Testpublic void testReflexMethod() throwsException {long start =System.currentTimeMillis();
Class testUserClass= Class.forName("RefleDemo.TestUser");
TestUser testUser=(TestUser) testUserClass.newInstance();
Method method= testUserClass.getMethod("sayHi");int i = 0;while(i<100000000){++i;
method.setAccessible(true);
method.invoke(testUser);
}long end =System.currentTimeMillis();
System.out.println("setAccessible=true 反射調(diào)用方法耗時:"+(end - start ) + "ms");
}//setAccessible=true 反射調(diào)用方法耗時:188ms
這里我們反射調(diào)用sayHi方法1億次,在調(diào)用了method.setAccessible(true)后,發(fā)現(xiàn)快了將近一半。查看API可以了解到,jdk在設(shè)置獲取字段,調(diào)用方法的時候會執(zhí)行安全訪問檢查,而此類操作會比較耗時,所以通過setAccessible(true)的方式可以關(guān)閉安全檢查,從而提升反射效率。
極致的反射
除了上面的手段,還有沒有什么辦法可以更極致的使用反射呢?這里介紹一個高性能反射工具包ReflectASM。它是通過字節(jié)碼生成的方式來實現(xiàn)的反射機制,下面是一個跟java反射的性能比較。
這里就不介紹它的用法了,有興趣的朋友可以直接傳送過去:https://github.com/EsotericSoftware/reflectasm
結(jié)語
最后總結(jié)一下,為了更好的使用反射,我們應(yīng)該在項目啟動的時候?qū)⒎瓷渌枰南嚓P(guān)配置及數(shù)據(jù)加載進(jìn)內(nèi)存中,在運行階段都從緩存中取這些元數(shù)據(jù)進(jìn)行反射操作。大家也不用懼怕反射,虛擬機在不斷的優(yōu)化,只要我們方法用的對,它并沒有”傳聞“中的那么慢,當(dāng)我們對性能有極致追求的時候,可以考慮通過三方包,直接對字節(jié)碼進(jìn)行操作。
---------------------------------------------------------
公眾號博文同步Github倉庫,有興趣的朋友可以幫忙給個Star哦,碼字不易,感謝支持。
https://github.com/PeppaLittlePig/blog-wechat
推薦閱讀
看完本文有收獲?請轉(zhuǎn)發(fā)分享給朋友吧
關(guān)注「深夜里的程序猿」,分享最干的干貨
總結(jié)
以上是生活随笔為你收集整理的java 反射的效率_如何提高使用Java反射的效率?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 希尔排序 最坏时间_排序算法(2)
- 下一篇: e5cc温控仪通讯参数设定_自动化工程师