淺析kryo

kryo是一個(gè)高性能的序列化/反序列化工具,由于其變長(zhǎng)存儲(chǔ)特性并使用了字節(jié)碼生成機(jī)制秦士,擁有較高的運(yùn)行速度和較小的體積隧土。

依賴

引入maven依賴

<dependency>
    <groupId>com.esotericsoftware</groupId>
    <artifactId>kryo</artifactId>
    <version>4.0.2</version>
</dependency>

需要注意的是,由于kryo使用了較高版本的asm辐脖,可能會(huì)與業(yè)務(wù)現(xiàn)有依賴的asm產(chǎn)生沖突皆愉,這是一個(gè)比較常見的問(wèn)題艇抠。只需將依賴改成:

<dependency>
    <groupId>com.esotericsoftware</groupId>
    <artifactId>kryo-shaded</artifactId>
    <version>4.0.2</version>
</dependency>

記錄類型信息

這算是kryo的一個(gè)特點(diǎn)家淤,可以把對(duì)象信息直接寫到序列化數(shù)據(jù)里瑟由,反序列化的時(shí)候可以精確地找到原始類信息,不會(huì)出錯(cuò)绿鸣,這意味著在寫readxxx方法時(shí)暂氯,無(wú)需傳入Class或Type類信息。

相應(yīng)的擎厢,kryo提供兩種讀寫方式辣吃。記錄類型信息的writeClassAndObject/readClassAndObject方法,以及傳統(tǒng)的writeObject/readObject方法厘惦。

線程安全

kryo的對(duì)象本身不是線程安全的哩簿,所以我們有兩種選擇來(lái)保障線程安全。

使用Threadlocal來(lái)保障線程安全:

private static final ThreadLocal<Kryo> kryoLocal = new ThreadLocal<Kryo>() {
    protected Kryo initialValue() {
        Kryo kryo = new Kryo();
        kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(
                    new StdInstantiatorStrategy()));
        return kryo;
    };
};

或者使用kryo提供的pool:

public KryoPool newKryoPool() {
        return new KryoPool.Builder(() -> {
            final Kryo kryo = new Kryo();
            kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(
                    new StdInstantiatorStrategy()));
            return kryo;
        }).softReferences().build();
    }

實(shí)例化器

在上面注意到kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy())); 這句話顯示指定了實(shí)例化器羡玛。

在一些依賴了kryo的開源軟件中稼稿,可能由于實(shí)例化器指定的問(wèn)題而拋出空指針異常讳窟。例如hive的某些版本中,默認(rèn)指定了StdInstantiatorStrategy是越。

public static ThreadLocal<Kryo> runtimeSerializationKryo = new ThreadLocal<Kryo>() {
    @Override
    protected synchronized Kryo initialValue() {
      Kryo kryo = new Kryo();
      kryo.setClassLoader(Thread.currentThread().getContextClassLoader());
      kryo.register(java.sql.Date.class, new SqlDateSerializer());
      kryo.register(java.sql.Timestamp.class, new TimestampSerializer());
      kryo.register(Path.class, new PathSerializer());
      kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
      ......
      return kryo;
    };
  };

而StdInstantiatorStrategy在是依據(jù)JVM version信息及JVM vendor信息創(chuàng)建對(duì)象的碌上,可以不調(diào)用對(duì)象的任何構(gòu)造方法創(chuàng)建對(duì)象。

那么例如碰到ArrayList這樣的對(duì)象時(shí)候天梧,就會(huì)出問(wèn)題霞丧。觀察一下ArrayList的源碼:

public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

既然沒(méi)有調(diào)用構(gòu)造器蛹尝,那么這里elementData會(huì)是NULL,那么在調(diào)用類似ensureCapacity方法時(shí)突那,就會(huì)拋出一個(gè)異常愕难。

 public void ensureCapacity(int minCapacity) {
        if (minCapacity > elementData.length
            && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
                 && minCapacity <= DEFAULT_CAPACITY)) {
            modCount++;
            grow(minCapacity);
        }
    }

解決方案很簡(jiǎn)單,就如框架中代碼寫的一樣葱弟,顯示指定實(shí)例化器猜丹,首先使用默認(rèn)無(wú)參構(gòu)造策略DefaultInstantiatorStrategy,若創(chuàng)建對(duì)象失敗再采用StdInstantiatorStrategy妖混。

類注冊(cè)

當(dāng)kryo寫一個(gè)對(duì)象的實(shí)例的時(shí)候轮洋,默認(rèn)需要將類的完全限定名稱寫入。將類名一同寫入序列化數(shù)據(jù)中是比較低效的祥楣,所以kryo支持通過(guò)類注冊(cè)進(jìn)行優(yōu)化汉柒。

kryo.register(SomeClassA.class);
kryo.register(SomeClassB.class);
kryo.register(SomeClassC.class);

注冊(cè)會(huì)給每一個(gè)class一個(gè)int類型的Id相關(guān)聯(lián),這顯然比類名稱高效兽间,但同時(shí)要求反序列化的時(shí)候的Id必須與序列化過(guò)程中一致正塌。這意味著注冊(cè)的順序非常重要恤溶。

但是由于現(xiàn)實(shí)原因咒程,同樣的代碼讼育,同樣的Class在不同的機(jī)器上注冊(cè)編號(hào)任然不能保證一致,所以多機(jī)器部署時(shí)候反序列化可能會(huì)出現(xiàn)問(wèn)題饥瓷。

所以kryo默認(rèn)會(huì)禁止類注冊(cè)痹籍,當(dāng)然如果想要打開這個(gè)屬性,可以通過(guò)kryo.setRegistrationRequired(true);打開刺洒。

循環(huán)引用

這是對(duì)循環(huán)引用的支持吼砂,可以有效防止棧內(nèi)存溢出,kryo默認(rèn)會(huì)打開這個(gè)屬性因俐。當(dāng)你確定不會(huì)有循環(huán)引用發(fā)生的時(shí)候周偎,可以通過(guò)kryo.setReferences(false);關(guān)閉循環(huán)引用檢測(cè)蓉坎,從而提高一些性能。

可變長(zhǎng)存儲(chǔ)

kryo對(duì)int和long類型都采用了可變長(zhǎng)存儲(chǔ)的機(jī)制蛉艾,以int為例勿侯,一般需要4個(gè)字節(jié)去存儲(chǔ),而對(duì)kryo來(lái)說(shuō)助琐,可以通過(guò)1-5個(gè)變長(zhǎng)字節(jié)去存儲(chǔ)兵钮,從而避免高位都是0的浪費(fèi)舌界。

最多需要5個(gè)字節(jié)存儲(chǔ)是因?yàn)楹铰蓿谧冮L(zhǎng)存儲(chǔ)int過(guò)程中粥血,一個(gè)字節(jié)的8位用來(lái)存儲(chǔ)有效數(shù)字的只有7位酿箭,最高位用于標(biāo)記是否還需讀取下一個(gè)字節(jié),1表示需要缔御,0表示不需要妇蛀。

在對(duì)string的存儲(chǔ)中也有變長(zhǎng)存儲(chǔ)的應(yīng)用,string序列化的整體結(jié)構(gòu)為length+內(nèi)容眷茁,那么length也會(huì)使用變長(zhǎng)int寫入字符的長(zhǎng)度纵诞。

如果使用緩存

在實(shí)際開發(fā)中,class增刪字段是很常見的事情浙芙,但對(duì)于kryo來(lái)說(shuō),確是不支持的纸俭,而如果恰好需要使用緩存南窗,那么這個(gè)問(wèn)題會(huì)被放得更大。

例如一個(gè)對(duì)象使用kryo序列化后女轿,數(shù)據(jù)放入了緩存中壕翩,而這時(shí)候如果這個(gè)對(duì)象增刪了一個(gè)屬性放妈,那么緩存中反序列化的時(shí)候就會(huì)報(bào)錯(cuò)荐操。所以頻繁使用緩存的場(chǎng)景珍策,可以盡量避免kryo。

不過(guò)現(xiàn)在的Kryo提供了兼容性的支持屯耸,使用CompatibleFieldSerializer.class蹭劈,在kryo.writeClassAndObject時(shí)候?qū)懭氲男畔⑷缦?

class name|field length|field1 name|field2 name|field1 value| filed2 value

而在讀入kryo.readClassAndObject時(shí)铺韧,會(huì)先讀入field names,然后匹配當(dāng)前反序列化類的field和順序再構(gòu)造結(jié)果塔逃。

當(dāng)然如果在做好緩存隔離的情況下料仗,這一切都不用在意。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末淹仑,一起剝皮案震驚了整個(gè)濱河市匀借,隨后出現(xiàn)的幾起案子平窘,更是在濱河造成了極大的恐慌,老刑警劉巖是鬼,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件均蜜,死亡現(xiàn)場(chǎng)離奇詭異芒率,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)充择,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門椎麦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人琴儿,你說(shuō)我怎么就攤上這事键兜∷氡茫” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)履肃。 經(jīng)常有香客問(wèn)我尺棋,道長(zhǎng),這世上最難降的妖魔是什么膘螟? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任荆残,我火速辦了婚禮内斯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘俘闯。我一直安慰自己真朗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布秀菱。 她就那樣靜靜地躺著,像睡著了一般赶么。 火紅的嫁衣襯著肌膚如雪脊串。 梳的紋絲不亂的頭發(fā)上琼锋,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音怖侦,去河邊找鬼谜叹。 笑死荷腊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的猜年。 我是一名探鬼主播疾忍,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼锭碳,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了推汽?” 一聲冷哼從身側(cè)響起歧沪,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎暖夭,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體竭望,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡咬清,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年旧烧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了画髓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡夺谁,死狀恐怖予权,靈堂內(nèi)的尸體忽然破棺而出浪册,到底是詐尸還是另有隱情,我是刑警寧澤岗照,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布村象,位于F島的核電站,受9級(jí)特大地震影響攒至,放射性物質(zhì)發(fā)生泄漏厚者。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一迫吐、第九天 我趴在偏房一處隱蔽的房頂上張望库菲。 院中可真熱鬧,春花似錦志膀、人聲如沸熙宇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至戳稽,卻和暖如春馆蠕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工互躬, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留播赁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓吼渡,卻偏偏與公主長(zhǎng)得像行拢,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子诞吱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理舟奠,服務(wù)發(fā)現(xiàn),斷路器房维,智...
    卡卡羅2017閱讀 134,656評(píng)論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法沼瘫,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法咙俩,繼承相關(guān)的語(yǔ)法耿戚,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 31,631評(píng)論 18 399
  • 2018年1月29日 農(nóng)歷丁酉年 臘月十三 星期一 忌:冬朝空心 《抱樸子養(yǎng)生論》: “冬朝勿空心阿趁∧せ祝”即冬天的早上...
    朵娘說(shuō)閱讀 541評(píng)論 0 1
  • 自互聯(lián)網(wǎng)流行至今以來(lái)皂股,很多企業(yè)都在做網(wǎng)上的推廣,企業(yè)做推廣的方式各種各樣命黔,大站營(yíng)銷是其中一種重要的推廣方式呜呐,它的發(fā)...
    housheng閱讀 114評(píng)論 0 0
  • 塵埃羈絆少余閑, 濁氣盈身強(qiáng)歡顏悍募, ...
    東籬笑東君閱讀 192評(píng)論 0 0