雲栖号資訊:【 點選檢視更多行業資訊】
在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!
本文主要梳理 Kryo 序列化基本實作。重點剖析 Kryo # writeClassAndObject、Kryo # readClassAndObject 方法。
1、源碼分析Kryo#writeClassAndObject
public void writeClassAndObject (Output output, Object object) {
if (output == null) throw new IllegalArgumentException("output cannot be null.");
beginObject(); // @1
try {
if (object == null) {
writeClass(output, null); // @2
return;
}
Registration registration = writeClass(output, object.getClass()); // @3
if (references && writeReferenceOrNull(output, object, false)) { // @4
registration.getSerializer().setGenerics(this, null);
return;
}
if (TRACE || (DEBUG && depth == 1)) log("Write", object);
registration.getSerializer().write(this, output, object); // @5
} finally {
if (--depth == 0 && autoReset) reset(); // @6
}
}
代碼@1:開始序列化,将dept自增,表示目前深度,因為在序列化一個對象時,該方法有可能會被遞歸調用,每遞歸調用增加1,一次調用結束後在finally字句中自減。
代碼@2:如果對象為空,則調用 writeClass ( DefaultSerializers $ ClassSerializer ),序列化為空。
代碼@3:如果對象不為空,首先序列化對象所屬的 Class 執行個體,從這裡可以看出,Kryo 在序列化時,首先先序列化類型。
代碼@4:如果 references 為 true (預設為 true,可以序列化循環依賴),則調用 writeReferenceOrNull 序列化。
代碼@5:如果 references 為 false,則調用 write 序列化,此時如果對象存在循環依賴,則會抛出 throw new KryoException("Max depth exceeded: " + depth ) 異常,如果 object 為基本類型,也将通過該方法完成值的序列化。
代碼@6:完成序列化後,恢複相關資料。也就是說 Kryo 執行個體并不是線程安全的。
預設 references 為 true,表示支援循環嵌套,我們接下來重點跟蹤一下 writeReferenceOrNull 方法。
1.1 源碼分析writeReferenceOrNull方法
/** @param object May be null if mayBeNull is true.
* @return true if no bytes need to be written for the object. */
boolean writeReferenceOrNull (Output output, Object object, boolean mayBeNull) { // @1
if (object == null) { // @2
if (TRACE || (DEBUG && depth == 1)) log("Write", null);
output.writeVarInt(Kryo.NULL, true);
return true;
}
if (!referenceResolver.useReferences(object.getClass())) { // @3
if (mayBeNull) output.writeVarInt(Kryo.NOT_NULL, true);
return false;
}
// Determine if this object has already been seen in this object graph.
int id = referenceResolver.getWrittenId(object); // @4
// If not the first time encountered, only write reference ID.
if (id != -1) { // @5
if (DEBUG) debug("kryo", "Write object reference " + id + ": " + string(object));
output.writeVarInt(id + 2, true); // + 2 because 0 and 1 are used for NULL and NOT_NULL.
return true;
}
// Otherwise write NOT_NULL and then the object bytes.
id = referenceResolver.addWrittenObject(object); // @6
output.writeVarInt(NOT_NULL, true);
if (TRACE) trace("kryo", "Write initial object reference " + id + ": " + string(object));
return false; // @7
}
代碼@1:參數說明:Output output:輸出流;Object object:待序列化的對象;
代碼@2:如果對象為空,寫入 Kryo.NULL(0),然後傳回 true,表示需要設定 generic,後續會講解一下 generic(泛型支援)。
代碼@3:如果是基本類型,如果 maybe (值可能為空),但該方法不為空,則設定為 Kryo.NOT_NULL ,然後傳回 false,表示非引用類型,需要持久化值。
代碼@4:判斷該對象是否在對象圖中已被序列化一次。(其實作方式 ListReferenceResolver、MapReferenceResolver)。

代碼@5:如果 writtenId 不等于 -1,表示該對象已被序列化,直接序列化 ID,直接傳回 true,然後結束 writeClassAndObject該方法,表示該對象執行個體完成。
代碼@6:為 object 建構一 個 ID,這個 ID 資料是在一次嵌套調用 writeClassAndObject 内有效,然後 writeClassAndObject 結束後,會調用 reset 方法,将其清空,然後先寫入為空辨別,并傳回 false,也就是第一次序列化對象時,傳回 false,會進入到 writeClassAndObject 的代碼@5中。
Kryo#writeClassAndObject
代碼@5
registration.getSerializer().write(this, output, object); // @5
複制代碼
其
實其重點關鍵,還是 writeClassAndObject # writeClass 也就是上文說的代碼 @3,在序列化對象之前,首先先序列化該對象的類型,然後需要傳回對應的字段序列器。例如,如果類的類型為 java.util.Map,則首先先要記錄類型為 Map,然後傳回可以序列化 Map 的序列器,再例如類型如果是 java.lang.String,則先序列化類型,然後序列化值,序列化值的序列器則為 DefaultSerializers$StringSerializer,那如果是一個對象類型,例如 cn.uce.demo.Student,自然,第一步是先序列化類型 cn.uce.demo.Student,接下來就需要序列化 Student 的各個字段的資訊,傳回的序列化為 DefaultSerializers$FieldSerializer,然後可以通過 FieldSeriaizer 傳回 Student 的屬性清單,然後單獨一個字段一個字段的序列化,其順序也就是,先類型,再序列化值。這樣就遞歸完成了一個對象的序列化操作。
Kryo序列化實作原理:
1、先序列化類型(Class執行個體),然後根據類型傳回相應的序列化器(上一篇詳細介紹了各種類型的序列化器)。
2、再序列化該類型的值。
3、如果自定義類型,例如(cn.uce.demo.Student),則傳回的值序列化器為DefaultSerializers$FieldSerializer,然後一個字段一個字段的序列化,當然其序列化類型也是,先類型再值的模式,遞歸進行,最終完成。
4、引入了對象圖的概念來消除循環依懶的序列化,已序列化的對象,在循環引用時,隻是用一個int類型來表示該對象值,類似一種緩存的概念。
Kryo與java 序列化的差別
kryo 的設計目的是指對象值的序列化,關注的是資料有效資料的傳輸,減少需要序列化的中繼資料資訊。
這一點通過 Kryo 對 Class 對象的序列化,也就是類型的序列化就能看出端倪。
Kryo 對 Class 的序列化隻需要化 Class 的全路徑名,在反序列化時根據 Class 通過類加載進行加載,大大減少了序列化後的檔案大小,能極大提高性能。
Kryo 的核心設計理念就是盡最大可能減少序列化後的檔案大小,其舉措1就是通過對long,int等資料類型,采用變長位元組存儲來代替java中使用固定位元組(4,8)位元組的模式,因為在軟體開發中,對象的這些值基本上都是小值,能節省很多空間,第二個舉措是使用了類似緩存的機制,在一次序列化對象中,在整個遞歸序列化期間,相同的對象,隻會序列化一次,後續的用一個局部int值來代替。
【雲栖号線上課堂】每天都有産品技術專家分享!
課程位址:
https://yqh.aliyun.com/live立即加入社群,與專家面對面,及時了解課程最新動态!
【雲栖号線上課堂 社群】
https://c.tb.cn/F3.Z8gvnK
原文釋出時間:2020-04-24
本文作者:中間件興趣圈
本文來自:“
掘金”,了解相關資訊可以關注“掘金”