Java反射與ReflectASM效能對比
阿新 • • 發佈:2022-05-19
這是幾年前寫的舊文,此前釋出Wordpress小站上,現在又重新整理。算是溫故知新,後續會繼續整理。如有錯誤望及時指出,在此感謝。
在日常的開發中,我們經常會用到反射。Java語言提供的反射機制能非常方便的讓我們在執行時進動態呼叫和修改。同時第三方ReflectASM也是位元組碼生成工具,可以達到反射的效果。
那麼今天就來比較下兩者的效能差異。
需求描述
比較Java反射與ReflectASM的效能差異
環境準備
Jkd1.8
//Maven依賴 <dependency> <groupId>com.esotericsoftware</groupId> <artifactId>reflectasm</artifactId> <version>1.11.0</version> </dependency>
測試場景
準備一個POJO,通過反射機制在執行時不斷修改類的例項,最後統計耗時
程式碼
import com.esotericsoftware.reflectasm.FieldAccess; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Iterator; import java.util.Set; class CarPojo { String carColor; String carName; int carSize; boolean isSale; public String getCarColor() { return carColor; } public void setCarColor(String carColor) { this.carColor = carColor; } public String getCarName() { return carName; } public void setCarName(String carName) { this.carName = carName; } public int getCarSize() { return carSize; } public void setCarSize(int carSize) { this.carSize = carSize; } public boolean isSale() { return isSale; } public void setIsSale(boolean sale) { isSale = sale; } @Override public String toString() { return "Car{" + "carColor='" + carColor + '\'' + ", carName='" + carName + '\'' + ", carSize=" + carSize + ", isSale=" + isSale + '}'; } } class DataTypeDesc implements Serializable { private static final long serialVersionUID = -6661934820005192771L; String fieldName; String fieldType; String fieldValue; public String getFieldName() { return fieldName; } public void setFieldName(String fieldName) { this.fieldName = fieldName; } public String getFieldType() { return fieldType; } public void setFieldType(String fieldType) { this.fieldType = fieldType; } public String getFieldValue() { return fieldValue; } public void setFieldValue(String fieldValue) { this.fieldValue = fieldValue; } public DataTypeDesc() { } public DataTypeDesc(String fieldName, String fieldType, String fieldValue) { this.fieldName = fieldName; this.fieldType = fieldType; this.fieldValue = fieldValue; } } /** * 通過反射進行物件賦值 */ public class JavaReflectTest1 { public static void main(String[] args) throws Exception { Set<DataTypeDesc> inputData = new HashSet<DataTypeDesc>(); DataTypeDesc d1 = new DataTypeDesc(); d1.setFieldName("carColor"); d1.setFieldType("STRING"); d1.setFieldValue("blue"); DataTypeDesc d2 = new DataTypeDesc(); d2.setFieldName("carName"); d2.setFieldType("STRING"); d2.setFieldValue("大眾"); DataTypeDesc d3 = new DataTypeDesc(); d3.setFieldName("carSize"); d3.setFieldType("INT"); d3.setFieldValue("11"); DataTypeDesc d4 = new DataTypeDesc(); d4.setFieldName("isSale"); d4.setFieldType("BOOLEAN"); d4.setFieldValue("true"); inputData.add(d1); inputData.add(d2); inputData.add(d3); inputData.add(d4); final int count = 10000000; long start_1 = System.nanoTime(); for (int i = 0; i < count; i++) { String str1 = testJdkReflect(CarPojo.class, inputData); } long stop_1 = System.nanoTime(); float duration1 = (stop_1 - start_1) / 1000000000.0F; System.out.println("testJdkReflect duration(s):" + duration1); //testJdkReflect duration(s):13.564455 long start_2 = System.nanoTime(); for (int i = 0; i < count; i++) { String str2 = testAsmReflect(CarPojo.class, inputData); } long stop_2 = System.nanoTime(); float duration2 = (stop_2 - start_2) / 1000000000.0F; System.out.println("testAsmReflect duration(s):" + duration2); //testAsmReflect duration(s):8.446463 System.out.println("duration diff(s):" + (duration1 - duration2) / duration1); //duration diff(s):0.3773091 } public static <T> String testJdkReflect(Class<T> clazz, Set<DataTypeDesc> inputData) throws Exception { //獲取類中宣告的所有欄位 // Field[] fields = clazz.getDeclaredFields(); //建立新的例項物件 T instance = clazz.newInstance(); DataTypeDesc dataType = null; Iterator<DataTypeDesc> iterator = inputData.iterator(); while (iterator.hasNext()) { //獲取欄位的名稱 // String fieldName = fields[i].getName(); dataType = iterator.next(); if (dataType != null) { String fieldName = dataType.getFieldName(); String fieldType = dataType.getFieldType(); String fieldValue = dataType.getFieldValue(); //把序列化id篩選掉 if (fieldName.equals("serialVersionUID")) { continue; } //獲取欄位的型別 Class<?> fieldTypeClass = clazz.getDeclaredField(fieldName).getType(); // 首字母大寫 String nameUpper = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); //獲得setter方法 Method setMethod = clazz.getMethod("set" + nameUpper, fieldTypeClass); //通過setter方法賦值給對應的成員變數 if (!fieldValue.isEmpty()) { //判斷讀取資料的型別 if (fieldType.equalsIgnoreCase("STRING") && fieldTypeClass.isAssignableFrom(String.class)) { //String處理 setMethod.invoke(instance, fieldValue); } if (fieldType.equalsIgnoreCase("INT") && (fieldTypeClass.isAssignableFrom(int.class) || fieldTypeClass.isAssignableFrom(Integer.class)) ) { //int處理 setMethod.invoke(instance, Integer.parseInt(fieldValue)); } if (fieldType.equalsIgnoreCase("BOOLEAN") && (fieldTypeClass.isAssignableFrom(Boolean.class) || fieldTypeClass.isAssignableFrom(boolean.class)) ) { //boolean處理 //這裡要注意,對於boolean型別,自動生成的set方法名可能會不符合我們的判斷,需要修正 setMethod.invoke(instance, Boolean.parseBoolean(fieldValue)); } } } } String json = instance.toString(); return json; } public static <T> String testAsmReflect(Class<T> clazz, Set<DataTypeDesc> inputData) throws Exception { T target = clazz.newInstance(); DataTypeDesc dataType = null; Iterator<DataTypeDesc> iterator = inputData.iterator(); FieldAccess fieldAccess = FieldAccess.get(target.getClass()); while (iterator.hasNext()) { dataType = iterator.next(); if (dataType != null) { String fieldName = dataType.getFieldName(); String fieldType = dataType.getFieldType(); String fieldValue = dataType.getFieldValue(); Object v = null; //判斷讀取資料的型別 if (fieldType.equalsIgnoreCase("STRING")) { //String處理 v = fieldValue; } if (fieldType.equalsIgnoreCase("INT")) { //int處理 v = Integer.parseInt(fieldValue); } if (fieldType.equalsIgnoreCase("BOOLEAN")) { //boolean處理 v = Boolean.parseBoolean(fieldValue); } fieldAccess.set(target, fieldName, v); } } String s = target.toString(); return s; } }
總結
這裡進行的測試並不嚴謹,沒有考慮讓JVM進行位元組碼執行時編譯等行為。從測試結果看出,使用Asm進行位元組碼生成的方式進行反射操作,還是要比Java自帶的反射效能要好35%以上。