Java序列化工具對比

1. Java序列化工具技術原理比較

  • Binary Formats & language-specific ones
    JavaBuiltIn(java原生)、JavaManual(根據成員變量類型,手工寫)、FstSerliazationKryo
  • Binary formats-generic language-unspecific ones
    Protobuf(Google)Thrift(Facebook)、 AvroGeneric、Hessian
  • JSON Format
    Jackson、Gson、FastJSON
  • JSON-like:
    CKS (textual JSON-like format)、BSON(JSON-like format with extended datatypes)、JacksonBson、MongoDB
  • XML-based formats
    XmlXStream

java的序列化工具大體就能夠分爲以上幾類,簡單歸納就分爲二進制binary和文本格式(json、xml)兩大類。
在速度的對比上通常有以下規律:
binary > textual
language-specific > language-unspecific
而textual中,由json相比xml冗餘度更低所以速度上更勝一籌,而json又bson這類textual serialization技術上更成熟,框架的選擇上更豐富和優秀。下面重點介紹下Kryo、fast-serialiation、fastjson、protocol-bufferhtml

2. 典型Java序列化工具分析

目前互聯網公司普遍使用Protobuf、Thrift、Avro等成熟的序列化解決方案來搭建RPC框架,這些都是久經考驗的解決方案。java

2.1 Java原生序列化工具

Java自己提供的序列化工具基本上能勝任大多數場景下的序列化任務,關於其序列化機制,這篇文章很細緻的解釋了(https://blog.csdn.net/zhaozheng7758/article/details/7820018),值得一讀。Java自帶的序列化工具在序列化過程當中須要不只須要將對象的完整的class name記錄下來,還須要把該類的定義也都記錄下,包括全部其餘引用的類,這會是一筆很大的開銷,尤爲是僅僅序列化單個對象的時候。正由於java序列化機制會把全部meta-data記錄下來,所以當修改了類的所在的包名後,反序列化則會報錯。Java自帶序列化工具的性能問題總結以下:
一個single object的序列化會 遞歸地,連同全部成員變量(instsnce variables)一塊兒序列化了,這種默認機制很容易形成沒必要要的序列化開銷。
序列化和反序列化過程須要上面的這種機制去遞歸併用反射機制去尋找全部成員變量的信息,另外若是沒定義本身serialVersionUID的話,那麼對象及其餘變量都必須本身產生一個。上述過程開銷很大。
使用默認序列化機制,全部序列化類定義完整信息都會被記錄下來,包括全部包名、父類信息、以及成員變量linux

2.2 優化過的Java序列化工具

  1. kryo
    kryo根據上述Java原生序列化機制的一些問題,對了不少優化工做,並且提供了不少serializer,甚至封裝了Unsafe類型的序列化方式,更多關於Unsafe類型的序列化方式,請參考這裏,須要注意的是,jdk1.7之後,默認關閉unsafe的類(sun.misc.Unsafe)包。更多kryo介紹參考kryo的wiki.
  2. fast-serialization
    fst-serialozation相對來講是一個很新的序列化工具,雖然從2-1的評測上來看,速度於kryo有一些差距,但根據本人在生產環境上的場景上測試,效果幾乎於kryo一致,都能瞬間反序列化出內容並渲染

2.3 JSON

比較優秀的JSON解析工具的表現仍是比較好的,有些json解析工具甚至速度超過了一些二進制的序列化方式。web

2.4 Protocol-Buffer

Protocol buffers是一個用來序列化結構化數據的技術,支持多種語言諸如C++、Java以及Python語言,可使用該技術來持久化數據或者序列化成網絡傳輸的數據。相比較一些其餘的XML技術而言,該技術的一個明顯特色就是更加節省空間(以二進制流存儲)、速度更快以及更加靈活。
另外Protobuf支持的數據類型相對較少,不支持常量類型。因爲其設計的理念是純粹的展示層協議(Presentation Layer),目前並無一個專門支持Protobuf的RPC框架。編程

2.5 Thrift

Thrift是Facebook開源提供的一個高性能,輕量級RPC服務框架,其產生正是爲了知足當前大數據量、分佈式、跨語言、跨平臺數據通信的需求。 可是,Thrift並不只僅是序列化協議,而是一個RPC框架。 相對於JSON和XML而言,Thrift在空間開銷和解析性能上有了比較大的提高,對於對性能要求比較高的分佈式系統,它是一個優秀的RPC解決方案;可是因爲Thrift的序列化被嵌入到Thrift框架裏面, Thrift框架自己並無透出序列化和反序列化接口,這致使其很難和其餘傳輸層協議共同使用(例如HTTP)。json

2.6 Avro

Avro解析性能高而且序列化以後的數據很是簡潔,比較適合於高性能的序列化服務。網絡

Avro提供兩種序列化格式:JSON格式或者Binary格式。Binary格式在空間開銷和解析性能方面能夠和Protobuf媲美, JSON格式方便測試階段的調試。 Avro支持的數據類型很是豐富,包括C++語言裏面的union類型。Avro支持JSON格式的IDL和相似於Thrift和Protobuf的IDL(實驗階段),這二者之間能夠互轉。Schema能夠在傳輸數據的同時發送,加上JSON的自我描述屬性,這使得Avro很是適合動態類型語言。 Avro在作文件持久化的時候,通常會和Schema一塊兒存儲,因此Avro序列化文件自身具備自我描述屬性,因此很是適合於作Hive、Pig和MapReduce的持久化數據格式。對於不一樣版本的Schema,在進行RPC調用的時候,服務端和客戶端能夠在握手階段對Schema進行互相確認,大大提升了最終的數據解析速度。app

3.下面介紹幾種經常使用的Java序列化技術使用示例

KryoRegister、FST、Kryo、Gson、Fastjson、JDK框架

3.1 JDK

public static byte[] serialize(Object obj) {
    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        byte[] bs = baos.toByteArray();
        baos.close();
        oos.close();

        return bs;
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

public static Object deserialize(byte[] bits) {
    try {
        ByteArrayInputStream bais = new ByteArrayInputStream(bits);
        ObjectInputStream ois = new ObjectInputStream(bais);
        Object obj = ois.readObject();

        bais.close();
        ois.close();
        return obj;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

3.2 Fastjson

一個JSON庫涉及的最基本功能就是序列化和反序列化。Fastjson支持java bean的直接序列化。 使用com.alibaba.fastjson.JSON這個類進行序列化和反序列化。分佈式

public static String serialize(Object obj){
    String json = JSON.toJSONString(obj);
    return json;
}
public static Object deserialize(String json, Class<?> clazz){
    Object obj = JSON.parseObject(json, clazz);
    return obj;
}

3.3 FST

FST fast-serialization 是從新實現的 Java 快速對象序列化的開發包。序列化速度更快(2-10倍)、體積更小,並且兼容 JDK 原生的序列化。

Java 快速序列化庫 FST 已經發布了 2.0 版本,該版本的包名已經更改,沒法平滑升級。另外官方建議爲了穩定性考慮仍是使用最新的 1.58 版本爲好

static FSTConfiguration configuration = FSTConfiguration
           .createDefaultConfiguration();

public static byte[] serialize(Object obj){
     return configuration.asByteArray((Serializable)obj);
}
public static Object deserialize(byte[] sec){
    return configuration.asObject(sec);
}

3.4 Gson

這裏採用JSON格式同時使用採用Google的gson進行轉義.

static Gson gson = new Gson();

public static String serialize(Object obj){
    String json = gson.toJson(obj);
    return json;
}
public static Object deserialize(String json, Class<?> clazz){
    Object obj = gson.fromJson(json, clazz);
    return obj;
}

3.5 Jackson

Jackson庫(http://jackson.codehaus.org),是基於java語言的開源json格式解析工具,整個庫(使用最新的2.2版本)包含3個jar包:
jackson-core.jar——核心包(必須),提供基於「流模式」解析的API。
jackson-databind——數據綁定包(可選),提供基於「對象綁定」和「樹模型」相關API。
jackson-annotations——註解包(可選),提供註解功能。
相對於java json解析的其餘庫,諸如json-lib、gson包,Jackson具備如下優勢:
功能全面,提供多種模式的json解析方式,「對象綁定」使用方便,利用註解包能爲咱們開發提供不少便利。
性能較高,「流模式」的解析效率超過絕大多數相似的json包。
核心包:JsonPaser(json流讀取),JsonGenerator(json流輸出)。
數據綁定包:ObjectMapper(構建樹模式和對象綁定模式),JsonNode(樹節點)

public static String serialize(Object obj){
    ObjectMapper mapper = new ObjectMapper();
    String json = null;
    try {
        json = mapper.writeValueAsString(obj);
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return json;
}
public static Object deserialize(String json, Class<?> clazz){
    ObjectMapper mapper = new ObjectMapper();
    Object obj = null;
    try {
        obj = mapper.readValue(json, clazz);
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return obj;
}

3.6 Kryo 和 KryoRegister

Kryo的運行速度是java Serializable 的20倍左右
Kryo的文件大小是java Serializable的一半左右
Kryo有兩種模式:
一種是先註冊(regist),再寫對象,即writeObject函數,實際上若是不先註冊,在寫對象時也會註冊,併爲class分配一個id。
注意,若是是rpc,則必須兩端都按一樣的順序註冊,不然會出錯,由於必需要明確類對應的惟一id。
另外一種是寫類名及對象,即writeClassAndObject函數。
writeClassAndObject函數是先寫入(-1 + 2)(一個約定的數字),再寫入類ID(第一次要先寫-1,再寫類ID + 類名),寫入引用關係(見引用的實現),最後才寫真正的數據)。
注意每一次writeClassAndObject調用後信息都會清空,因此不用擔憂和client交互時會出錯。

static Kryo kryo = new Kryo();

public static byte[] serialize(Object obj) {
    byte[] buffer = new byte[2048];
    Output output = new Output(buffer);

    kryo.writeClassAndObject(output, obj);
    byte[] bs = output.toBytes();
    output.close();
    return bs;
}

public static Object deserialize(byte[] src) {
    Input input = new Input(src);
    Object obj = kryo.readClassAndObject(input);
    input.close();
    return obj;
}

register

static Kryo kryo = null;
static{
    kryo = new Kryo();
    kryo.setReferences(false);
    kryo.setRegistrationRequired(false);
    kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
}

public static byte[] serialize(Object obj) {
    kryo.register(obj.getClass());
    byte[] buffer = new byte[2048];
    Output output = new Output(buffer);
    kryo.writeObject(output, obj);
    byte[] bs = output.toBytes();
    output.close();
    return bs;
}

public static Object deserialize(byte[] src, Class<?> clazz) {
    kryo.register(clazz);
    Input input = new Input(src);
    Object obj = kryo.readObject(input, clazz);
    input.close();
    return obj;
}

Object Serializalbe 優勢:java原生支持,不須要提供第三方的類庫,使用比較簡單。缺點:沒法跨語言,字節數佔用比較大,某些狀況下對於對象屬性的變化比較敏感。
對象在進行序列化和反序列化的時候,必須實現Serializable接口,但並不強制聲明惟一的serialVersionUID,是否聲明serialVersionUID對於對象序列化的向上向下的兼容性有很大的影響。

4. 小結

就已有原先使用Java原生序列化方案的系統來講,kryo於fst-serializer是良好的java原生序列化方案替代者,不只體現再編程簡單,並且速度與性能上會有大幅提高,尤爲是fst-serializer ,只需替代output/inputstream 便可,性能的提高上也很可觀,目前該工具剛出來,穩定性還須要多測測。

若是程序自己就用json格式序列化,則能夠考慮引入一個性能優異的json解析庫,通常再服務端jackson是廣受歡迎的解析庫。

protobuffer更多的是一種取代xml的誇語言的消息交換格式,儘快速度很快,可是編程上須要定義消息格式,對成員變量多、業務複雜的javabean來講代價是較爲複雜的,對穩定的已有系統來講整體代價較高。

下表是幾種方案的各項指標的一個對比

序列化工具 序列化速度 序列化文件大小 編程模型複雜度 社區活躍度 jar包大小
kryo 極快 簡單 132kb
fst-serializer 很是簡單 246kb
protobuffer 較大 較複雜 穩定 329kb
fastjson 較快 較大 簡單 通常 338kb
jackson 通常 較大 簡單 穩定 1.1mb
gson 較慢 較大 簡單 穩定 189kb

參考:
http://blog.51cto.com/zlfwmm/1761401
https://blog.csdn.net/wodeyuer125/article/details/44495549
https://www.javacodegeeks.com/2010/07/java-best-practices-high-performance.html
http://www.javacodegeeks.com/2010/07/java-best-practices-high-performance.html
https://blog.csdn.net/zhaozheng7758/article/details/7820018
http://www.javacodegeeks.com/2012/07/native-cc-like-performance-for-java.html
https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/

相關文章
相關標籤/搜索