溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

何為序列化

發(fā)布時間:2021-10-25 17:26:30 來源:億速云 閱讀:116 作者:iii 欄目:編程語言

這篇文章主要講解了“何為序列化”,文中的講解內(nèi)容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“何為序列化”吧!

何為序列化 / 反序列化 

序列化

序列化就是一種處理對象流的機制。

即將對象的內(nèi)容流化,將數(shù)據(jù)轉(zhuǎn)化成字節(jié)流,以便存儲在文件中或用于在網(wǎng)絡中傳輸,當然用的最多的肯定就是網(wǎng)絡傳輸。 

反序列化

文件中或網(wǎng)絡上獲得序列化后的對象字節(jié)流后,根據(jù)字節(jié)流中所保存的對象狀態(tài)及描述信息,通過反序列化重建對象

大家所常見的序列化/反序列化的方式:Serializable。

誰敢說沒見過Serializable,給我拖出的吊樹上打。

JDK提供的Serializable速度較慢,原因比如:因為加入了序列化版本號,類名等信息,所以導致碼流變大,速度變慢等等。

所以我們要來學一些速度快的序列化方式。不僅速度快,占用的空間還小。

這么好的技術,那個妹子沒學到,真是可惜了。 

序列化選型標準

  • 通用性:是否只能用于java間序列化/反序列化,是否跨語言、跨平臺

  • 性能:分為空間開銷和時間開銷,序列化后的數(shù)據(jù)一般用于存儲或網(wǎng)絡傳輸,其大小是很重要的一個參數(shù);

當然解析的時間也影響了序列化協(xié)議的選擇

  • 易用性:API使用是否復雜,是否影響開發(fā)效率

  • 可擴展性:實體類的屬性變更會不會導致反序列化異常,這通常會在系統(tǒng)升級時會產(chǎn)生,參考性不是很大

 

Kryo序列化入門

Kryo 是一個快速序列化/反序列化工具,其使用了字節(jié)碼生成機制(底層依賴了 ASM 庫),因此具有比較好的運行速度。

Kryo 序列化出來的結果,是其自定義的、獨有的一種格式,不再是 JSON 或者其他現(xiàn)有的通用格式;

而且,其序列化出來的結果是二進制的(即 byte[];而 JSON 本質(zhì)上是字符串 String);

二進制數(shù)據(jù)顯然體積更小,序列化、反序列化時的速度也更快。

Kryo 一般只用來進行序列化(然后作為緩存,或者落地到存儲設備之中)、反序列化,而不用于在多個系統(tǒng)、甚至多種語言間進行數(shù)據(jù)交換 —— 目前 kryo 也只有 java 實現(xiàn)。

Redis這樣的存儲工具,是可以安全的存儲二進制數(shù)據(jù),所以一般項目中可使用Kryo來替代JDK序列化進行存儲。

使用場景:(數(shù)據(jù)交換或數(shù)據(jù)持久化)比如使用kryo把對象序列化成字節(jié)數(shù)組發(fā)送給消息隊列或者放到redis等nosql中等等應用場景。

 
pom配置
<dependency>
    <groupId>com.esotericsoftware</groupId>
    <artifactId>kryo</artifactId>
    <version>4.0.1</version>
</dependency>
 

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

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

代碼實現(xiàn)

注意:由于kryo不是線程安全的,針對多線程情況下的使用,要對kryo進行一個簡單的封裝設計,從而可以多線程安全的使用序列化和反序列化

 
接口
/**
 * 序列化工具(程序調(diào)用該接口來實現(xiàn)obj<->byte[]之間的序列化/反序列化)
 */
public interface Serializer{
 
 /**
  * 序列化
  */
 public void serialize(Object t,byte[] bytes);
 
 /**
  * 序列化
  */
 public void serialize(Object obj, byte[] bytes, int offset, int count);
 
 /**
  * 反序列化
  */
 public <T>T deserialize(byte[] bytes);

 /**
  * 反序列化
  */
 public <T>T deserialize(byte[] bytes, int offset, int count);
}
     
實現(xiàn)類
/**
 * 基于kyro的序列化/反序列化工具
 */
public class kryoSerializer implements Serializer {

 // 由于kryo不是線程安全的,所以每個線程都使用獨立的kryo
 final ThreadLocal<Kryo> kryoLocal = new ThreadLocal<Kryo>() {
  @Override
  protected Kryo initialValue() {
   Kryo kryo = new Kryo();
   kryo.register(ct, new BeanSerializer<>(kryo, ct));
   return kryo;
  }
 };
  
  // 序列化threadlocal
 final ThreadLocal<Output> outputLocal = new ThreadLocal<Output>();
  
  // 反序列化threadlocal
 final ThreadLocal<Input> inputLocal = new ThreadLocal<Input>();
  
 private Class<?> ct = null;

 public kryoSerializer(Class<?> ct) {
  this.ct = ct;
 }
  
  /**
  * 序列化
  */
 @Override
 public void serialize(Object obj, byte[] bytes) {
  Kryo kryo = getKryo();
  Output output = getOutput(bytes);
  kryo.writeObjectOrNull(output, obj, obj.getClass());
  output.flush();
 }

  /**
  * 序列化
  */
 @Override
 public void serialize(Object obj, byte[] bytes, int offset, int count) {
  Kryo kryo = getKryo();
  Output output = getOutput(bytes, offset, count);
  kryo.writeObjectOrNull(output, obj, obj.getClass());
  output.flush();
 }
  
  /**
  * 反序列化
  */
 @SuppressWarnings("unchecked")
 @Override
 public <T> T deserialize(byte[] bytes, int offset, int count) {
  Kryo kryo = getKryo();
  Input input = getInput(bytes, offset, count);
  return (T) kryo.readObjectOrNull(input, ct);
 }

 /**
  * 反序列化
  */
 @Override
 public <T> T deserialize(byte[] bytes) {
  return deserialize(bytes, 0, bytes.length);
 }

 /**
  * 獲取kryo
  */
 private Kryo getKryo() {
  return kryoLocal.get();
 }

 /**
  * 獲取Output并設置初始數(shù)組
  */
 private Output getOutput(byte[] bytes) {
  Output output = null;
  if ((output = outputLocal.get()) == null) {
   output = new Output();
   outputLocal.set(output);
  }
  if (bytes != null) {
   output.setBuffer(bytes);
  }
  return output;
 }

 /**
  * 獲取Output
  */
 private Output getOutput(byte[] bytes, int offset, int count) {
  Output output = null;
  if ((output = outputLocal.get()) == null) {
   output = new Output();
   outputLocal.set(output);
  }
  if (bytes != null) {
   output.writeBytes(bytes, offset, count);
  }
  return output;
 }

 /**
  * 獲取Input
  */
 private Input getInput(byte[] bytes, int offset, int count) {
  Input input = null;
  if ((input = inputLocal.get()) == null) {
   input = new Input();
   inputLocal.set(input);
  }
  if (bytes != null) {
   input.setBuffer(bytes, offset, count);
  }
  return input;
 }
  
  public Class<?> getCt() {
  return ct;
 }

 public void setCt(Class<?> ct) {
  this.ct = ct;
 }
     

Kryo的IO

Kryo致力以簡單易用的API,序列化過程中主要核心有Kryo、Output、Input。

Output和Input是Kryo的IO,他們支持以byte array或者stream的形式為序列化的dest和反序列化的source。

當使用stream形式進行寫出寫入時,需要close這些Output和Input。

寫出時,當OutputDe buffer是滿的時候,就會flush bytes到stream中。

寫入時,會從stream中獲取bytes到Input buffer中,當填充滿時,進行反序列化。

 

Kryo的注冊

和很多其他的序列化框架一樣,Kryo為了提供性能和減小序列化結果體積,提供注冊的序列化對象類的方式。

在注冊時,會為該序列化類生成int ID,后續(xù)在序列化時使用int ID唯一標識該類型。

注冊的方式如下:

kryo.register(SomeClass.class);
 

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

kryo.register(SomeClass.class, 1);
     

對象引用方面

這是對循環(huán)引用的支持,可以有效防止棧內(nèi)存溢出,kryo默認會打開這個屬性。

當你確定不會有循環(huán)引用發(fā)生的時候,可以通過以下代碼關閉循環(huán)引用檢測,從而提高一些性能,但不是很推薦

Kryo kryo = new Kryo();
kryo.setReferences(false);
     

Kryo的讀和寫的方式

  • 如果序列化的對象類型未知并且可能為空:

    kryo.writeClassAndObject(output, object);
    // ...
    Object object = kryo.readClassAndObject(input);
    if (object instanceof SomeClass) {
       // ...
    }
  • 如果對象類型已知并且可能為空:

    kryo.writeObjectOrNull(output, someObject);
    // ...
    SomeClass someObject = kryo.readObjectOrNull(input, SomeClass.class);
  • 如果對象類型已知并且不可能為空:

    kryo.writeObject(output, someObject);
    // ...
    SomeClass someObject = kryo.readObject(input, SomeClass.class);
 

序列化線程安全

Kryo默認是線程不安全的,有兩種解決方式:

  • 一個是通過Threadlocal的方式為某個線程存儲一個實例:

    private static final ThreadLocal<Kryo> kryoThreadLocal = new ThreadLocal<Kryo>() {
        protected Kryo initialValue() {
            Kryo kryo = new Kryo();
            // 這里可以增加一系列配置信息
            return kryo;
        }
    };
  • 另外一種是通過KryoPool的方式,該方式在性能上也好于ThreadLocal:

    public KryoPool createPool() {
        return new KryoPool.Builder(() -> {
            Kryo kryo = new Kryo();
            // 此處也可以進行一系列配置,可通過實現(xiàn)KryoFactory接口來滿足動態(tài)注冊,抽象該類
            return kryo;
        }).softReferences().build();
    }
 

kryo支持序列化的類型

booleanBooleanbyteBytechar
CharactershortShortintInteger
longLongfloatFloatdouble
Doublebyte[]StringBigIntegerBigDecimal
CollectionDateCollections.emptyListCollections.singletonMap
StringBuilderTreeMapCollections.emptyMapCollections.emptySetKryoSerializable
StringBufferClassCollections.singletonListCollections.singletonMapCurrency
CalendarTimeZoneEnumEnumSet
 

kryo優(yōu)缺點總結

 
優(yōu)點
  • 序列化的性能非常高
  • 序列化結果體積較小
  • 提供了簡單易用的API
 
缺點
  • 跨語言支持較復雜

  • 不支持對象字段的增加/刪除/修改

    如果更改了對象的字段,然后再從更改前序列化的bytes中反序列化,將會出錯。

    當然如果想得到Add、Remove等操作的支持,可以使用FieldSerializer的其他擴展,如TaggedFieldSerializer、VersionFieldSerializer等等。

 

kryo 與 JDK性能對比

 
Simple類
import java.io.Serializable;
import java.util.Map;

public class Simple implements Serializable {  
   private static final long serialVersionUID = -4914434736682797743L;  
   private String name;  
   private int age;  
   private Map<String,Integer> map;  

   public Simple(){  

   }  

   public Simple(String name,int age,Map<String,Integer> map){  
       this.name = name;  
       this.age = age;  
       this.map = map;  
   }  

   public String getName() {  
     return name;  
   }  

   public void setName(String name) {  
      this.name = name;  
   }  

   public int getAge() {  
      return age;  
   }  

   public void setAge(int age) {  
      this.age = age;  
   }  

   public Map<String, Integer> getMap() {  
      return map;  
   }  

   public void setMap(Map<String, Integer> map) {  
      this.map = map;  
   }
}
     

JDK性能測試

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;

public class OriginalSerializable {
      
  public static void main(String[] args) throws IOException, ClassNotFoundException {  
      long start =  System.currentTimeMillis();  
      setSerializableObject();  
      System.out.println("java原生序列化時間:" + (System.currentTimeMillis() - start) + " ms" );    
      start =  System.currentTimeMillis();  
      getSerializableObject();  
      System.out.println("java原生反序列化時間:" + (System.currentTimeMillis() - start) + " ms");  
  }
  
  public static void setSerializableObject() throws IOException{  

    FileOutputStream fo = new FileOutputStream("D:/file2.bin");  

    ObjectOutputStream so = new ObjectOutputStream(fo);  

    for (int i = 0; i < 100000; i++) {  
        Map<String,Integer> map = new HashMap<String, Integer>(2);  
        map.put("zhang0", i);  
        map.put("zhang1", i);  
        so.writeObject(new Simple("zhang"+i,(i+1),map));  
    }  
    so.flush();  
    so.close();  
  } 
  
  public static void getSerializableObject(){  
      FileInputStream fi;  
      try {  
        fi = new FileInputStream("D:/file2.bin");  
        ObjectInputStream si = new ObjectInputStream(fi);  

        Simple simple =null;  
        while((simple=(Simple)si.readObject()) != null){  
            //System.out.println(simple.getAge() + "  " + simple.getName());  
        }  
        fi.close();  
        si.close();  
      } catch (FileNotFoundException e) {  
          e.printStackTrace();  
      } catch (IOException e) {  
          //e.printStackTrace();  
      } catch (ClassNotFoundException e) {  
          e.printStackTrace();  
      }
  }
}
     

kryo性能測試

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.objenesis.strategy.StdInstantiatorStrategy;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.KryoException;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

public class KyroSerializable {  
      
    public static void main(String[] args) throws IOException {  
        long start =  System.currentTimeMillis();  
        setSerializableObject();  
        System.out.println("Kryo 序列化時間:" + (System.currentTimeMillis() - start) + " ms" );  
        start =  System.currentTimeMillis();  
        getSerializableObject();  
        System.out.println("Kryo 反序列化時間:" + (System.currentTimeMillis() - start) + " ms");  
  
    }  
  
    public static void setSerializableObject() throws FileNotFoundException{  
  
        Kryo kryo = new Kryo();  
        kryo.setReferences(false);  
        kryo.setRegistrationRequired(false);  
        kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());  
        kryo.register(Simple.class);  
        Output output = new Output(new FileOutputStream("D:/file1.bin"));  
        for (int i = 0; i < 100000; i++) {  
            Map<String,Integer> map = new HashMap<String, Integer>(2);  
            map.put("zhang0", i);  
            map.put("zhang1", i);  
            kryo.writeObject(output, new Simple("zhang"+i,(i+1),map));  
        }  
        output.flush();  
        output.close();  
    }  
  
  
    public static void getSerializableObject(){  
        Kryo kryo = new Kryo();  
        kryo.setReferences(false);  
        kryo.setRegistrationRequired(false);  
        kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());  
        Input input;  
        try {  
            input = new Input(new FileInputStream("D:/file1.bin"));  
            Simple simple =null;  
            while((simple=kryo.readObject(input, Simple.class)) != null){  
                //System.out.println(simple.getAge() + "  " + simple.getName() + "  " + simple.getMap().toString());  
            }  
  
            input.close();  
        } catch (FileNotFoundException e) {  
            e.printStackTrace();  
        } catch(KryoException e){  
  
        }  
    }
}
     

測試結果

 
JDK
  • java原生序列化時間:6614 ms
  • java原生反序列化時間:8609 ms
何為序列化  
 
kryo
  • Kryo 序列化時間:757 ms
  • Kryo 反序列化時間:307 ms
何為序列化

感謝各位的閱讀,以上就是“何為序列化”的內(nèi)容了,經(jīng)過本文的學習后,相信大家對何為序列化這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI