欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java高性能序列化工具Kryo詳情

 更新時(shí)間:2022年06月10日 10:33:28   作者:??JAVA旭陽????  
這篇文章主要介紹了Java高性能序列化工具Kryo詳情,Kryo?是一個(gè)快速序列化/反序列化工具,依賴于字節(jié)碼生成機(jī)制,更多相關(guān)內(nèi)容感興趣的朋友可以參考一下下面文章內(nèi)容

概述

Kryo 是一個(gè)快速序列化/反序列化工具,依賴于字節(jié)碼生成機(jī)制(底層使用了 ASM 庫),因此在序列化速度上有一定的優(yōu)勢,但正因如此,其使用也只能限制在基于 JVM 的語言上。

和 Hessian 類似,Kryo 序列化出的結(jié)果,是其自定義的、獨(dú)有的一種格式。由于其序列化出的結(jié)果是二進(jìn)制的,也即 byte[],因此像 Redis 這樣可以存儲二進(jìn)制數(shù)據(jù)的存儲引擎是可以直接將 Kryo 序列化出來的數(shù)據(jù)存進(jìn)去。當(dāng)然你也可以選擇轉(zhuǎn)換成 String 的形式存儲在其他存儲引擎中(性能有損耗)。

基礎(chǔ)用法

介紹了這么多,接下來我們就來看看 Kryo 的基礎(chǔ)用法吧。其實(shí)對于序列化框架來說,API 基本都差不多,畢竟入?yún)⒑统鰠⑼ǔ6际谴_定的(需要序列化的對象/序列化的結(jié)果)。在使用 Kryo 之前,我們需要引入相應(yīng)的依賴。

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

基本使用如下所示:

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import java.io.*;
public class HelloKryo {
   static public void main(String[] args) throws Exception {
       Kryo kryo = new Kryo();
       kryo.register(SomeClass.class);
       SomeClass object = new SomeClass();
       object.value = "Hello Kryo!";
       Output output = new Output(new FileOutputStream("file.bin"));
       kryo.writeObject(output, object);
       output.close();

       Input input = new Input(new FileInputStream("file.bin"));
       SomeClass object2 = kryo.readObject(input, SomeClass.class);
       input.close();
       System.out.println(object2.value);
  }
   static public class SomeClass {
       String value;
  }
}

Kryo 類會(huì)自動(dòng)執(zhí)行序列化。Output 類和 Input 類負(fù)責(zé)處理緩沖字節(jié),并寫入到流中。如果序列化前和序列化后類的字段不一致,反序列化會(huì)失敗。

Kryo 的序列化

作為一個(gè)靈活的序列化框架,Kryo 并不關(guān)心讀寫的數(shù)據(jù),作為開發(fā)者,你可以隨意使用 Kryo 提供的那些開箱即用的序列化器。

Kryo 的注冊

和很多其他的序列化框架一樣,Kryo 為了提供性能和減小序列化結(jié)果體積,提供注冊的序列化對象類的方式。在注冊時(shí),會(huì)為該序列化類生成 int ID,后續(xù)在序列化時(shí)使用 int ID 唯一標(biāo)識該類型。

注冊的方式如下:

kryo.register(SomeClass.class);

或者

kryo.register(SomeClass.class,?1);

可以明確指定注冊類的 int ID,但是該 ID 必須大于等于 0。如果不提供,內(nèi)部將會(huì)使用 int++的方式維護(hù)一個(gè)有序的 int ID 生成。

Kryo 的序列化器

Kryo 支持多種序列化器,通過源碼我們可窺知一二

具體可參考 

雖然 Kryo 提供的序列化器可以讀寫大多數(shù)對象,但開發(fā)者也可以輕松的制定自己的序列化器。篇幅限制,這里就不展開說明了,僅以默認(rèn)的序列化器為例。

對象引用

在新版本的 Kryo 中,默認(rèn)情況下是不啟用對象引用的。這意味著如果一個(gè)對象多次出現(xiàn)在一個(gè)對象圖中,它將被多次寫入,并將被反序列化為多個(gè)不同的對象。

舉個(gè)例子,當(dāng)開啟了引用屬性,每個(gè)對象第一次出現(xiàn)在對象圖中,會(huì)在記錄時(shí)寫入一個(gè) varint,用于標(biāo)記。當(dāng)此后有同一對象出現(xiàn)時(shí),只會(huì)記錄一個(gè) varint,以此達(dá)到節(jié)省空間的目標(biāo)。此舉雖然會(huì)節(jié)省序列化空間,但是是一種用時(shí)間換空間的做法,會(huì)影響序列化的性能,這是因?yàn)樵趯懭?讀取對象時(shí)都需要進(jìn)行追蹤。

開發(fā)者可以使用 kryo 自帶的 setReferences 方法來決定是否啟用 Kryo 的引用功能。

public class KryoReferenceDemo {
    public static void main(String[] args) throws FileNotFoundException {
        Kryo kryo = new Kryo();
        kryo.register(User.class);
        kryo.register(Account.class);
        User user = new User();
        user.setUsername("alvin");
        Account account = new Account();
        account.setAccountNo("10000");
        // 循環(huán)引用
        user.setAccount(account);
        account.setUser(user);

        // 這里需要設(shè)置為true,才不會(huì)報(bào)錯(cuò)
        kryo.setReferences(true);

        Output output = new Output(new FileOutputStream("kryoreference.bin"));
        kryo.writeObject(output, user);
        output.close();

        Input input = new Input(new FileInputStream("kryoreference.bin"));
        User object2 = kryo.readObject(input, User.class);
        input.close();
        System.out.println(object2.getUsername());
        System.out.println(object2.getAccount().getAccountNo());
    }

    public static class User {
        private String username;
        private Account account;
        public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public Account getAccount() {
            return account;
        }
        public void setAccount(Account account) {
            this.account = account;
        }
    }
    public static class Account {
        private String accountNo;
        private String amount;
        private User user;
        public String getAccountNo() {
            return accountNo;
        }
       public void setAccountNo(String accountNo) {
            this.accountNo = accountNo;
        }
        public String getAmount() {
            return amount;
        }
        public void setAmount(String amount) {
            this.amount = amount;
        }
        public User getUser() {
            return user;
        }
        public void setUser(User user) {
            this.user = user;
        }
    }
}

如果序列化前的setReferences(false), 后面設(shè)置setReferences(true)進(jìn)行反序列化,會(huì)失敗。

線程不安全

Kryo 不是線程安全的。每個(gè)線程都應(yīng)該有自己的 Kryo 對象、輸入和輸出實(shí)例。

因此在多線程環(huán)境中,可以考慮使用 ThreadLocal 或者對象池來保證線程安全性。

ThreadLocal + Kryo 解決線程不安全

ThreadLocal 是一種典型的犧牲空間來換取并發(fā)安全的方式,它會(huì)為每個(gè)線程都單獨(dú)創(chuàng)建本線程專用的 kryo 對象。對于每條線程的每個(gè) kryo 對象來說,都是順序執(zhí)行的,因此天然避免了并發(fā)安全問題。創(chuàng)建方法如下:

static?private?final?ThreadLocal<Kryo>?kryos?=?new?ThreadLocal<Kryo>() {
??protected?Kryo?initialValue() {
? ? ?Kryo?kryo?=?new?Kryo();
? ? ?// 在此處配置kryo對象的使用示例,如循環(huán)引用等
? ? ?return?kryo;
? };
};

Kryo?kryo?=?kryos.get();

之后,僅需要通過 kryos.get() 方法從線程上下文中取出對象即可使用。

對象池 + Kryo 解決線程不安全

「池」是一種非常重要的編程思想,連接池、線程池、對象池等都是

「復(fù)用」思想的體現(xiàn),通過將創(chuàng)建的“對象”保存在某一個(gè)“容器”中,以便后續(xù)反復(fù)使用,避免創(chuàng)建、銷毀的產(chǎn)生的性能損耗,以此達(dá)到提升整體性能的作用。

Kryo 對象池原理也是如此。Kryo 框架自帶了對象池的實(shí)現(xiàn),整個(gè)使用過程不外乎創(chuàng)建池、從池中獲取對象、歸還對象三步,以下為代碼實(shí)例。

// Pool constructor arguments: thread safe, soft references, maximum capacity
Pool<Kryo>?kryoPool?=?new?Pool<Kryo>(true,?false,?8) {
??protected?Kryo?create?() {
? ? ?Kryo?kryo?=?new?Kryo();
? ? ?// Kryo 配置
? ? ?return?kryo;
? }
};

// 獲取池中的Kryo對象
Kryo?kryo?=?kryoPool.obtain();
// 將kryo對象歸還到池中
kryoPool.free(kryo);

創(chuàng)建 Kryo 池時(shí)需要傳入三個(gè)參數(shù),其中第一個(gè)參數(shù)用于指定是否在 Pool 內(nèi)部使用同步,如果指定為 true,則允許被多個(gè)線程并發(fā)訪問。第三個(gè)參數(shù)適用于指定對象池的大小的,這兩個(gè)參數(shù)較容易理解,因此重點(diǎn)來說一下第二個(gè)參數(shù)。

如果將第二個(gè)參數(shù)設(shè)置為 true,Kryo 池將會(huì)使用 java.lang.ref.SoftReference 來存儲對象。這允許池中的對象在 JVM 的內(nèi)存壓力大時(shí)被垃圾回收。Pool clean 會(huì)刪除所有對象已經(jīng)被垃圾回收的軟引用。當(dāng)沒有設(shè)置最大容量時(shí),這可以減少池的大小。當(dāng)池子有最大容量時(shí),沒有必要調(diào)用 clean,因?yàn)槿绻_(dá)到了最大容量,Pool free 會(huì)嘗試刪除一個(gè)空引用。

創(chuàng)建完 Kryo 池后,使用 kryo 就變得異常簡單了,只需調(diào)用 kryoPool.obtain() 方法即可,使用完畢后再調(diào)用 kryoPool.free(kryo) 歸還對象,就完成了一次完整的租賃使用。

理論上,只要對象池大小評估得當(dāng),就能在占用極小內(nèi)存空間的情況下完美解決并發(fā)安全問題。如果想要封裝一個(gè) Kryo 的序列化方法,可以參考如下的代碼

public?static?byte[]?serialize(Object?obj) {
? ?Kryo?kryo?=?kryoPool.obtain();
? ?// 使用 Output 對象池會(huì)導(dǎo)致序列化重復(fù)的錯(cuò)誤(getBuffer返回了Output對象的buffer引用)
? ?try?(Output?opt?=?new?Output(1024,?-1)) {
? ? ? ?kryo.writeClassAndObject(opt,?obj);
? ? ? ?opt.flush();
? ? ? ?return?opt.getBuffer();
? }finally?{
? ? ? ?kryoPool.free(kryo);
? }
}

小結(jié)

相較于 JDK 自帶的序列化方式,Kryo 的性能更快,并且由于 Kryo 允許多引用和循環(huán)引用,在存儲開銷上也更小。只不過,雖然 Kryo 擁有非常好的性能,但其自身卻舍去了很多特性,例如線程安全、對序列化對象的字段修改等。雖然這些弊端可以通過 Kryo 良好的擴(kuò)展性得到一定的滿足,但是對于開發(fā)者來說仍然具有一定的上手難度,不過這并不能影響其在 Java 中的地位。

到此這篇關(guān)于Java高性能序列化工具Kryo詳情的文章就介紹到這了,更多相關(guān) Java Kryo序列化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 優(yōu)化Java代碼中if-else的方案分享

    優(yōu)化Java代碼中if-else的方案分享

    代碼可讀性是衡量代碼質(zhì)量的重要標(biāo)準(zhǔn),可讀性也是可維護(hù)性、可擴(kuò)展性的保證,而我們在編程時(shí)常常會(huì)發(fā)現(xiàn)代碼中有大量if?else語句,如何進(jìn)行優(yōu)化呢,下面就來和大家詳細(xì)聊聊
    2023-05-05
  • BigDecimal的toString()、toPlainString()和toEngineeringString()區(qū)別及用法詳解

    BigDecimal的toString()、toPlainString()和toEngineeringString()區(qū)

    使用BigDecimal進(jìn)行打印的時(shí)候,經(jīng)常會(huì)對BigDecimal提供的三個(gè)toString方法感到好奇,以下整理3個(gè)toString方法的區(qū)別及用法,需要的朋友可以參考下
    2023-08-08
  • Maven Spring jar包啟動(dòng)報(bào)錯(cuò)問題解決方案

    Maven Spring jar包啟動(dòng)報(bào)錯(cuò)問題解決方案

    maven 編譯jar包,放在linux服務(wù)器啟動(dòng)不起來,提示:xxxx-0.0.1-SNAPSHOT.jar中沒有主清單屬性,接下來通過本文給大家分享問題原因及解決方案,感興趣的朋友跟隨小編一起看看吧
    2023-10-10
  • 在SpringBoot中使用jwt實(shí)現(xiàn)token身份認(rèn)證的實(shí)例代碼

    在SpringBoot中使用jwt實(shí)現(xiàn)token身份認(rèn)證的實(shí)例代碼

    你還不會(huì)在SpringBoot中使用jwt實(shí)現(xiàn)token身份認(rèn)證嗎,本文小編就給大家詳細(xì)的介紹一下在SpringBoot中使用jwt實(shí)現(xiàn)token身份認(rèn)證的實(shí)例代碼,感興趣的同學(xué)可以自己動(dòng)手試一試
    2023-09-09
  • springbatch的封裝與使用實(shí)例詳解

    springbatch的封裝與使用實(shí)例詳解

    主要實(shí)現(xiàn)批量數(shù)據(jù)的處理,我對batch進(jìn)行的封裝,提出了jobBase類型,具體job需要實(shí)現(xiàn)它即可。這篇文章主要介紹了springbatch的封裝與使用 ,需要的朋友可以參考下
    2019-04-04
  • 詳解java中的PropertyChangeSupport與PropertyChangeListener

    詳解java中的PropertyChangeSupport與PropertyChangeListener

    這篇文章主要介紹了詳解java中的PropertyChangeSupport與PropertyChangeListener的相關(guān)資料,需要的朋友可以參考下
    2017-09-09
  • idea與eclipse項(xiàng)目相互導(dǎo)入的過程(圖文教程)

    idea與eclipse項(xiàng)目相互導(dǎo)入的過程(圖文教程)

    這篇文章主要介紹了idea與eclipse項(xiàng)目相互導(dǎo)入的過程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • 手把手帶你實(shí)現(xiàn)第一個(gè)Mybatis程序

    手把手帶你實(shí)現(xiàn)第一個(gè)Mybatis程序

    這篇文章主要介紹了mybatis實(shí)現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2021-07-07
  • Java?Spring?Boot請求方式與請求映射過程分析

    Java?Spring?Boot請求方式與請求映射過程分析

    這篇文章主要介紹了Java?Spring?Boot請求方式與請求映射過程分析,Spring?Boot支持Rest風(fēng)格:使用HTTP請求方式的動(dòng)詞來表示對資源的操作
    2022-06-06
  • Java中Json與List、Map、entity的互相轉(zhuǎn)化

    Java中Json與List、Map、entity的互相轉(zhuǎn)化

    在開發(fā)中,Json轉(zhuǎn)換的場景往往也就是那么幾個(gè),本文主要介紹了Java中Json與List、Map、entity的互相轉(zhuǎn)化,具有一定的參考價(jià)值,感興趣的可以了解一下
    2022-07-07

最新評論