1. 程式人生 > >objenesis的實現與效能測試

objenesis的實現與效能測試

最近看kryo的原始碼發現使用了一個叫做objenesis的java庫,主要是用在建立物件上面,它可以不用呼叫建構函式就建立物件。由於並不是所有的java類都有無參建構函式,並且有的類的建構函式還是private的,所以更甚有些類是第三方的,我們不能修改原始碼,所以這個庫還是很有用的。http://objenesis.org/details.html

objenisis包含很多平臺和jvm的實現,這裡我們只關心sun的jvm中的Instantiator的實現。其中的實現大約有三種:

  • SunReflectionFactoryInstantiator:使用sun.reflect.ReflectionFactory來建立物件
  • SunReflectionFactorySerializationInstantiator:也是使用sun.reflect.ReflectionFactory來建立物件,但是為了相容序列化,需要找到第一個沒有實現serializable介面的父類,效能有一定的損耗。
  • UnsafeFactoryInstantiator:使用sun.misc.Unsafe.allocateInstance來建立物件

還有一種MagicInstantiator看上去沒什麼用。

SunReflectionFactoryInstantiator

我們先來看看SunReflectionFactoryInstantiator的實現,程式碼非常短。

public SunReflectionFactoryInstantiator(Class<T> type) {
//獲得object的物件
      Constructor<Object> javaLangObjectConstructor = getJavaLangObjectConstructor();
      //這個函式是關鍵,主要是為這個class建立了一個新的constractor
      mungedConstructor = SunReflectionFactoryHelper.newConstructorForSerialization(
          type, javaLangObjectConstructor);
      mungedConstructor.setAccessible(true
); } //建立物件,大家都看的懂 public T newInstance() { try { return mungedConstructor.newInstance((Object[]) null); } catch(Exception e) { throw new ObjenesisException(e); } } //獲得object的物件 private static Constructor<Object> getJavaLangObjectConstructor() { try { return Object.class.getConstructor((Class[]) null); } catch(NoSuchMethodException e) { throw new ObjenesisException(e); } }

從上面的程式碼可以看到SunReflectionFactoryHelper.newConstructorForSerialization函式是關鍵,我們去看看這個原始碼:

 public static <T> Constructor<T> newConstructorForSerialization(Class<T> type,
      Constructor<?> constructor) {
      //下面兩行這裡主要就是獲得sun.reflect.ReflectionFactory的物件
      Class<?> reflectionFactoryClass = getReflectionFactoryClass();
      Object reflectionFactory = createReflectionFactory(reflectionFactoryClass);

    //獲得newConstructorForSerialization方法的method物件
      Method newConstructorForSerializationMethod = getNewConstructorForSerializationMethod(
         reflectionFactoryClass);

//利用反射呼叫newConstructorForSerialization方法獲得新的constructor物件
      try {
         return (Constructor<T>) newConstructorForSerializationMethod.invoke(
            reflectionFactory, type, constructor);
      }
      catch(IllegalArgumentException e) {
         throw new ObjenesisException(e);
      }
      catch(IllegalAccessException e) {
         throw new ObjenesisException(e);
      }
      catch(InvocationTargetException e) {
         throw new ObjenesisException(e);
      }
   }

註釋寫的很清楚,關鍵就在sun.reflect.ReflectionFactory的newConstructorForSerialization方法中,實際上這個方法返回的是一個無參的constructor物件,但是絕對不會與原來的constructor衝突,被稱為munged 建構函式。

SunReflectionFactorySerializationInstantiator

我們來看看實現

public class SunReflectionFactorySerializationInstantiator<T> implements ObjectInstantiator<T> {

   private final Constructor<T> mungedConstructor;

   public SunReflectionFactorySerializationInstantiator(Class<T> type) {
       //獲得父類中第一個沒有實現serializable介面的類的class物件
      Class<? super T> nonSerializableAncestor = SerializationInstantiatorHelper
         .getNonSerializableSuperClass(type);

      //獲得無參建構函式
      Constructor<? super T> nonSerializableAncestorConstructor;
      try {
         nonSerializableAncestorConstructor = nonSerializableAncestor
            .getConstructor((Class[]) null);
      }
      catch(NoSuchMethodException e) {
         throw new ObjenesisException(new NotSerializableException(type+" has no suitable superclass constructor"));         
      }
        //獲得munged建構函式物件,這個建構函式中會呼叫nonSerializableAncestorConstructor
      mungedConstructor = SunReflectionFactoryHelper.newConstructorForSerialization(
         type, nonSerializableAncestorConstructor);
      mungedConstructor.setAccessible(true);
   }

   public T newInstance() {
      try {
         return mungedConstructor.newInstance((Object[]) null);
      }
      catch(Exception e) {
         throw new ObjenesisException(e);
      }
   }
}

程式碼比較簡單,唯一有疑問的地方是SerializationInstantiatorHelper.getNonSerializableSuperClass(type);我們來看看其中的實現

 public static <T> Class<? super T> getNonSerializableSuperClass(Class<T> type) {
      Class<? super T> result = type;
      while(Serializable.class.isAssignableFrom(result)) {
         result = result.getSuperclass();
         if(result == null) {
            throw new Error("Bad class hierarchy: No non-serializable parents");
         }
      }
      return result;

   }

看的出來其實就是迴圈去找第一個沒有實現Serializable介面的父類的class物件。

UnsafeFactoryInstantiator

實現程式碼:

 private static Unsafe unsafe;
   private final Class<T> type;

   public UnsafeFactoryInstantiator(Class<T> type) {
   //初始化unsafe物件,不然不能使用。
      if (unsafe == null) {
         Field f;
         try {
            f = Unsafe.class.getDeclaredField("theUnsafe");
         } catch (NoSuchFieldException e) {
            throw new ObjenesisException(e);
         }
         f.setAccessible(true);
         try {
            unsafe = (Unsafe) f.get(null);
         } catch (IllegalAccessException e) {
            throw new ObjenesisException(e);
         }
      }
      this.type = type;
   }

//使用unsafe.allocateInstance的方法建立物件,然後進行強制轉換。
   public T newInstance() {
      try {
         return type.cast(unsafe.allocateInstance(type));
      } catch (InstantiationException e) {
         throw new ObjenesisException(e);
      }
   }

程式碼很簡單。

效能測試

objenesis中有benchmark,我在我的windows電腦上跑了一下:

環境:

  • 12GB記憶體
  • oracle jdk 64位 1.8
  • i5
  • windows 8

結果

CreateObject.createObjectWithConstructor(new)             avgt   20        6.122
         ±      1.672  ns/op
CreateObject.createObjectWithConstructor(newInstance)    avgt   20        7.614
         ±      1.672  ns/op
CreateObject.createObjectWithMungedConstructor            avgt   20        8.415
 ±      0.252  ns/op
CreateObject.createObjectWithMungedConstructorRaw         avgt   20       10.413
 ±      1.187  ns/op
CreateObject.createObjectWithMungedConstructorRawAndCast  avgt   20       10.644
 ±      0.186  ns/op
CreateObject.createObjectWithUnsafe                       avgt   20       29.813
 ±      1.885  ns/op
CreateObject.createObjectWithUnsafeRaw                    avgt   20       26.572
 ±      0.261  ns/op
CreateObject.createObjectWithUnsafeRawAndCast             avgt   20       27.153
 ±      1.336  ns/op
CreateObject.createObjectWithUnsafeRawException           avgt   20       27.390
 ±      1.090  ns/op
  1. MungedConstructor最快,比new只慢了2ns,比newInstance慢了1ns。
  2. unsafe很慢,花費的時間大約是MungedConstructor的3.5倍,是new的5倍,是newinstance的4倍。