寫一些Java架構的時候,經常需要通過反射get或者set某個bean的field,比較普通的做法是擷取field後調用java.lang.reflect.Field.get(Object),但每次都這樣調用,能否有最佳化的空間呢?
答案是有。
第一種:
由於每次都是重複的調用,所以想到了緩衝每個bean的field,但這樣做還是不夠,所以想到了寫一個code generator。通過產生代碼的方式,get或者set每個bean的時候直接調用該bean的getter或者setter,這個實現聽起來很牛逼,其實就是用asm產生一個類在用一個classloader載入進來每次調用直接invoke就可以了。
可單純為了一個反射調用做這麼多,總感覺是大炮打了蚊子。
第二種:
多謝@RednaxelaFX 的指點,找到了更簡單的做法:sun.misc.Unsafe
使用也非常的簡單:首先通過sun.misc.Unsafe.objectFieldOffset(Field) 擷取field的offset,然後使用sun.misc.Unsafe.getObject(Object, long)擷取某個執行個體上的field的值。
Hessian在現實對Java對象的序列化和還原序列化的時候都是使用這種方式!具體可以參見com.caucho.hessian.io.HessianInput、com.caucho.hessian.io.HessianOutput。這幾種使用方式的效率究竟相差多大?後續再找個時間來測試下!
對sun.misc.Unsafe 簡單的測試代碼如下:
import java.io.Serializable;import java.lang.reflect.Field;import sun.misc.Unsafe;/** * @author haitao.yao Dec 14, 2010 */public class ReflectionCompare {private static final int count = 10000000;/** * @param args */public static void main(String[] args) {long duration = testIntCommon();System.out.println("int common test for " + count+ " times, duration: " + duration);duration = testUnsafe();System.out.println("int unsafe test for " + count+ " times, duration: " + duration);}private static long testUnsafe() {long start = System.currentTimeMillis();sun.misc.Unsafe unsafe = getUnsafe();int temp = count;Field field = getIntField();long offset = unsafe.objectFieldOffset(field);while (temp-- > 0) {unsafe.getInt(new TestBean(), offset);}return System.currentTimeMillis() - start;}private static long testIntCommon() {long start = System.currentTimeMillis();int temp = count;getIntField().setAccessible(true);while (temp-- > 0) {TestBean bean = new TestBean();try {getIntField().get(bean);} catch (Exception e) {e.printStackTrace();}}return System.currentTimeMillis() - start;}private static final sun.misc.Unsafe unsafe;static {sun.misc.Unsafe value = null;try {Class<?> clazz = Class.forName("sun.misc.Unsafe");Field field = clazz.getDeclaredField("theUnsafe");field.setAccessible(true);value = (Unsafe) field.get(null);} catch (Exception e) {e.printStackTrace();throw new RuntimeException("error to get theUnsafe", e);}unsafe = value;}public static final sun.misc.Unsafe getUnsafe() {return unsafe;}private static final Field intField;private static final Field stringField;static {try {intField = TestBean.class.getDeclaredField("age");stringField = TestBean.class.getDeclaredField("name");} catch (Exception e) {e.printStackTrace();throw new IllegalStateException("failed to init testbean field", e);}}public static final Field getIntField() {return intField;}public static final Field getStringField() {return stringField;}/** * @author haitao.yao Dec 14, 2010 */static class TestBean implements Serializable {/*** */private static final long serialVersionUID = -5994966479456252766L;private String name;private int age;/** * @return the name */public String getName() {return name;}/** * @param name * the name to set */public void setName(String name) {this.name = name;}/** * @return the age */public int getAge() {return age;}/** * @param age * the age to set */public void setAge(int age) {this.age = age;}}}
【原文】http://hi.baidu.com/hellolq/item/26493d1efe2b066a71d5e89d