Java序列化是Java技術體系當中的一個重要議題,序列化和反序列化是在應對網絡編程最常遇到的問題之一。序列化的意義在於信息的交換和存儲,一般會和io、持久化、rmi技術有關(eg:一些orm框架會要求持久化的對象類型實現Serializable接口),咱們也能夠序列化時將Java Object轉成byte[];反序列化時將byte[]轉成Java Object。及Protostuff的實現方法。
此時,可能有些朋友已經想到了JDK serializable的序列化方式,既然JDK已經有現成的方法了,爲何還要使用Protostuff ? 咱們先對常見的序列化方式作一下對比你就知道了。java
序列化框架性能對比(kryo、hessian、java、protostuff)git
簡介:github
|
優勢編程 |
缺點json |
Kryo數組 |
速度快,序列化後體積小緩存 |
跨語言支持較複雜網絡 |
Hessian框架 |
默認支持跨語言jvm |
較慢 |
Protostuff |
速度快,基於protobuf |
需靜態編譯 |
Protostuff-Runtime |
無需靜態編譯,但序列化前需預先傳入schema |
不支持無默認構造函數的類,反序列化時需用戶本身初始化序列化後的對象,其只負責將該對象進行賦值 |
Java |
使用方便,可序列化全部類 |
速度慢,佔空間 |
測試環境:
硬件信息:
16 Intel(R) Xeon(R) CPU E5620 @2.40GHz
Red Hat Enterprise Linux Server release 5.4 (Tikanga)
java: "1.6.0_27" Java HotSpot(TM) 64-Bit Server VM (build 20.2-b06, mixed mode)
JVM options: java -Xmx256m –server
測試數據:(見附件)
ArrayList.class
MediaContent.class
Media.class
Image.class
測試方法:(參考自https://github.com/eishay/jvm-serializers)
<!--[if !supportLists]-->一、 <!--[endif]-->在正式測試以前,將測試用例運行10次對JVM進行預熱。
<!--[if !supportLists]-->二、 <!--[endif]-->對測試用例的每一個方法,運行2000次,取平均值。
<!--[if !supportLists]-->三、 <!--[endif]-->每次測試用例運行500次,取最優結果
測試基準:
ser: 建立一個對象,並將其序列化成byte數組的時間
deser: 將byte數組反序列化成對象的時間
total: 建立一個對象,將其序列化成byte數組再反序列化爲對象的總時間
size: 序列化後的數組大小
size+dfl: 序列化後用level6級別的zlib進行壓縮後的大小
測試工具:
序列化工具 |
序列化方式 |
kryo |
使用kryo默認的序列化方式fieldSerializer, 對須要序列化的對象採起默認的操做。開啓reference,關閉register |
protostuff |
使用靜態編譯生成的Schema進行序列化 |
protostuff-runtime |
使用protostuff-runtime框架生成Schema進行序列化 |
測試結果:
時間:
大小:
總結:
Kryo在類註冊且reference關閉的狀況下,序列化速度和大小明顯 優於hessian和java,接近於protostuff。開啓reference後將序列化速度將明顯變慢,但仍舊優於hessian。
相關知識:
類註冊:將須要序列化的類註冊到kryo中,能夠提升序列化與反序列化的速度。
Reference:開啓這個選項後,相同的對象將被序列化爲同一個byte[],默認關閉,若是要支持循環引用,則必須開啓
穩定性測試:
測試用例(見附件)
循環引用:Cyclic.java
序列化方式 |
無默認構造函數 |
循環引用 |
對象爲null |
是否須要預先知道對象所屬的類 |
大對象(4M) |
Kryo |
支持 |
需將reference選項打開 |
支持 |
不須要,關閉register |
支持 |
Java |
支持 |
支持 |
支持 |
不須要 |
支持 |
Protostuff |
支持 |
支持 |
支持 |
不須要 |
支持 |
Protostuff -runtime |
不支持 |
支持 |
支持 |
須要 |
支持 |
Hessian |
支持 |
支持 |
支持 |
不須要 |
支持 |
經過對比咱們發現Protostuff仍是很是強悍的,性能、穩定性、友好性都是很是好得,那麼就讓咱們學習一下Protostuff吧。
Protostuff的項目主頁:http://www.protostuff.io/
Protostuff是一個序列化庫,支持一下序列化格式:
序列化
@SuppressWarnings("unchecked") public static <T> byte[] serialize(T obj) { Class<T> cls = (Class<T>) obj.getClass(); LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); try { Schema<T> schema = getSchema(cls); return ProtostuffIOUtil.toByteArray(obj, schema, buffer); } catch (Exception e) { throw new IllegalStateException(e.getMessage(), e); } finally { buffer.clear(); } }
第3行:得到對象的類;
第4行:使用LinkedBuffer分配一塊默認大小的buffer空間;
第6行:經過對象的類構建對應的schema;
第7行:使用給定的schema將對象序列化爲一個byte數組,並返回。
反序列化
public static <T> T deserialize(byte[] data, Class<T> cls) { try { T message = objenesis.newInstance(cls); Schema<T> schema = getSchema(cls); ProtostuffIOUtil.mergeFrom(data, message, schema); return message; } catch (Exception e) { throw new IllegalStateException(e.getMessage(), e); } }
第3行:使用objenesis實例化一個類的對象;
第4行:經過對象的類構建對應的schema;
第5,6行:使用給定的schema將byte數組和對象合併,並返回。
構建schema
構建schema的過程可能會比較耗時,所以但願使用過的類對應的schema能被緩存起來。代碼以下,再也不贅述:
private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<>(); private static <T> Schema<T> getSchema(Class<T> cls) { Schema<T> schema = (Schema<T>) cachedSchema.get(cls); if (schema == null) { schema = RuntimeSchema.createFrom(cls); if (schema != null) { cachedSchema.put(cls, schema); } } return schema; }
能夠看到方法第4行使用了RuntimeSchema,關於RuntimeSchema的用法參考例子:
eg:
<!--protostuff序列化依賴--> <dependency> <groupId>com.dyuproject.protostuff</groupId> <artifactId>protostuff-core</artifactId> <version>1.1.2</version> </dependency> <dependency> <groupId>com.dyuproject.protostuff</groupId> <artifactId>protostuff-runtime</artifactId> <version>1.1.2</version> </dependency>
package com.jhaso.shopping.util; import com.dyuproject.protostuff.LinkedBuffer; import com.dyuproject.protostuff.ProtostuffIOUtil; import com.dyuproject.protostuff.Schema; import com.dyuproject.protostuff.runtime.RuntimeSchema; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * @author 俠客人生 * @ClassName ProtostuffUtil * @Description Protostuff序列化工具類 * @create 2017-08-10 22:23 * @version: V1.0.0 **/ public class ProtostuffUtil { private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>(); private static <T> Schema<T> getSchema(Class<T> clazz) { @SuppressWarnings("unchecked") Schema<T> schema = (Schema<T>) cachedSchema.get(clazz); if (schema == null) { schema = RuntimeSchema.getSchema(clazz); if (schema != null) { cachedSchema.put(clazz, schema); } } return schema; } /** * 序列化 * * @param obj * @return */ public static <T> byte[] serializer(T obj) { @SuppressWarnings("unchecked") Class<T> clazz = (Class<T>) obj.getClass(); LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); try { Schema<T> schema = getSchema(clazz); return ProtostuffIOUtil.toByteArray(obj, schema, buffer); } catch (Exception e) { throw new IllegalStateException(e.getMessage(), e); } finally { buffer.clear(); } } /** * 反序列化 * * @param data * @param clazz * @return */ public static <T> T deserializer(byte[] data, Class<T> clazz) { try { T obj = clazz.newInstance(); Schema<T> schema = getSchema(clazz); ProtostuffIOUtil.mergeFrom(data, obj, schema); return obj; } catch (Exception e) { throw new IllegalStateException(e.getMessage(), e); } } }