Java各種反射效能對比
對各種方法實現get方法的效能進行了一個測試。
總共有5個測試,,每個測試都是執行1億次
1. 直接通過Java的get方法
2.通過高效能的ReflectAsm庫進行測試
3.通過Java Class類自帶的反射獲得Method測試
4.使用Java自帶的Property類獲取Method測試
5.BeanUtils的getProperty測試
1 測試用Bean類
測試定義瞭如下一個bean類。
public class SimpleBean { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
注意定義要嚴格遵守JavaBean規範,否則在使用和反射相關工具時會出現NoSuchMethodException異常,或者導致效能非常差,JavaBean規範中最重要的幾點如下:
1.類必須是public, 擁有public無參構造器,這樣能夠通過反射newInstance()動態構建物件. String className = ...; Class beanClass = Class.forName(className); Object beanInstance = beanClass.newInstance(); 2.因為反射newInstance使用的是無參構造器, 所以物件例項化和配置是分開的 3.每一個property都有一個public的getter和setter方法, 命名方式是get/set+首字母大寫的property名
經測試在SimpleBean為public時,1億次呼叫method.invoke方法:
javaReflectGet 100000000 times using 218 ms
而SimpleBean為預設包可見時,1一億次呼叫method.invoke方法:
javaReflectGet 100000000 times using 12955 ms
2.測試程式碼
public class TestIterator { private long times = 100_000_000L; private SimpleBean bean; private String formatter = "%s %d times using %d ms"; @Before public void setUp() throws Exception { bean = new SimpleBean(); bean.setName("haoyifen"); } //直接通過Java的get方法 @Test public void directGet() { Stopwatch watch = Stopwatch.createStarted(); for (long i = 0; i < times; i++) { bean.getName(); } watch.stop(); String result = String.format(formatter, "directGet", times, watch.elapsed(TimeUnit.MILLISECONDS)); System.out.println(result); } //通過高效能的ReflectAsm庫進行測試,僅進行一次methodAccess獲取 @Test public void reflectAsmGet() { MethodAccess methodAccess = MethodAccess.get(SimpleBean.class); Stopwatch watch = Stopwatch.createStarted(); for (long i = 0; i < times; i++) { methodAccess.invoke(bean, "getName"); } watch.stop(); String result = String.format(formatter, "reflectAsmGet", times, watch.elapsed(TimeUnit.MILLISECONDS)); System.out.println(result); } //通過Java Class類自帶的反射獲得Method測試,僅進行一次method獲取 @Test public void javaReflectGet() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { Method getName = SimpleBean.class.getMethod("getName"); Stopwatch watch = Stopwatch.createStarted(); for (long i = 0; i < times; i++) { getName.invoke(bean); } watch.stop(); String result = String.format(formatter, "javaReflectGet", times, watch.elapsed(TimeUnit.MILLISECONDS)); System.out.println(result); } //使用Java自帶的Property屬性獲取Method測試,僅進行一次method獲取 @Test public void propertyGet() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, IntrospectionException { Method method = null; BeanInfo beanInfo = Introspector.getBeanInfo(SimpleBean.class); PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { if (propertyDescriptor.getName().equals("name")) { method = propertyDescriptor.getReadMethod(); break; } } Stopwatch watch = Stopwatch.createStarted(); for (long i = 0; i < times; i++) { method.invoke(bean); } watch.stop(); String result = String.format(formatter, "propertyGet", times, watch.elapsed(TimeUnit.MILLISECONDS)); System.out.println(result); } //BeanUtils的getProperty測試 @Test public void beanUtilsGet() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { Stopwatch watch = Stopwatch.createStarted(); for (long i = 0; i < times; i++) { BeanUtils.getProperty(bean, "name"); } watch.stop(); String result = String.format(formatter, "beanUtilsGet", times, watch.elapsed(TimeUnit.MILLISECONDS)); System.out.println(result); } }
3.測試結果
在4核[email protected]機器上跑以上測試,經過多次測量,基本在以下數值範圍附近,測試資料如下:
1. directGet 100000000 times using 37 ms
2. reflectAsmGet 100000000 times using 39 ms
3. javaReflectGet 100000000 times using 222 ms
4. propertyGet 100000000 times using 335 ms
5. beanUtilsGet 100000000 times using 20066 ms
4.結果分析
1.使用reflectAsm庫的效能能和直接呼叫get方法持平
2.Java自帶的反射效能大致為直接get的1/6和1/9.
3.BeanUtils的getProperty非常的慢,為直接get效能的1/500,為Java自帶反射效能的1/100和1/60.
轉載至:https://www.cnblogs.com/Frank-Hao/p/5839096.html