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
- MungedConstructor最快,比new只慢了2ns,比newInstance慢了1ns。
- unsafe很慢,花費的時間大約是MungedConstructor的3.5倍,是new的5倍,是newinstance的4倍。