Kryo 序列化說明

簡介

Kroy 是一個相對來講性能和序列化之後的大小,都是比較好的一個序列化框架,項目git地址:java

https://github.com/EsotericSoftware/kryogit

簡單的一個demo :

Log.set(1);   // 這裏是開啓kroy 的 trace的日誌功能,能夠看到具體的序列化和反序列化過程。

        Kryo kryo = new Kryo();

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        Output output = new Output(outputStream);

        User user = new User();

        user.setName("test");
        user.setId(12);
        user.setPassword("test");

        List list = new ArrayList();
        list.add("test");
        list.add("test");
        list.add("p");

        user.setOrders(list);


        kryo.writeObject(output, user);
//        kryo.writeClassAndObject(output, user);
        output.close();

        byte[] bytes = outputStream.toByteArray();

//        System.out.println("string == " + bytesToHexString(bytes));

        System.out.println("===============  " + bytes.length);

        System.out.println(new String(bytes));

        System.out.println(binary(bytes , 16));


        ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);

        Input input = new Input(inputStream);


        User copy = kryo.readObject(input , User.class);
        
//        User copy = (User) kryo.readClassAndObject(input);
        input.close();


        System.out.println(user);
        System.out.println(copy);

說明:

有關序列化的字段:

kroy是根據對應的序列化實體的全部的字段 , 進行序列化操做,不會調用你設置的 實體的 set字段的方法。
eg : 序列化的反序列化不會調用這裏的setName方法。
class User {
        String name ;
        
        public setName(){
                this.name = name + "_update";
        }
            
    }

必需要有一個空的構造方法:

若是你包含非空的構造方法,則,必須強制添加一個空的空的構造方法,能夠是私有的構造方法。

優勢說明:

有關register 說明:

在 kroy中,提供了register功能,register究竟是作什麼用的 ?若是開啓了kroy的日誌功能,咱們能夠看到以下的輸出:
:00 DEBUG: [kryo] Class not available: java.time.ZoneOffset
00:00 DEBUG: [kryo] Class not available: java.time.ZoneId
00:00 DEBUG: [kryo] Class not available: java.time.OffsetTime
00:00 DEBUG: [kryo] Class not available: java.time.OffsetDateTime
00:00 DEBUG: [kryo] Class not available: java.time.ZonedDateTime
00:00 DEBUG: [kryo] Class not available: java.time.Year
00:00 DEBUG: [kryo] Class not available: java.time.YearMonth
00:00 DEBUG: [kryo] Class not available: java.time.MonthDay
00:00 DEBUG: [kryo] Class not available: java.time.Period
00:00 TRACE: [kryo] Register class ID 0: int (com.esotericsoftware.kryo.serializers.DefaultSerializers$IntSerializer)
00:00 TRACE: [kryo] Register class ID 1: String (com.esotericsoftware.kryo.serializers.DefaultSerializers$StringSerializer)
00:00 TRACE: [kryo] Register class ID 2: float (com.esotericsoftware.kryo.serializers.DefaultSerializers$FloatSerializer)
00:00 TRACE: [kryo] Register class ID 3: boolean (com.esotericsoftware.kryo.serializers.DefaultSerializers$BooleanSerializer)
00:00 TRACE: [kryo] Register class ID 4: byte (com.esotericsoftware.kryo.serializers.DefaultSerializers$ByteSerializer)
00:00 TRACE: [kryo] Register class ID 5: char (com.esotericsoftware.kryo.serializers.DefaultSerializers$CharSerializer)
00:00 TRACE: [kryo] Register class ID 6: short (com.esotericsoftware.kryo.serializers.DefaultSerializers$ShortSerializer)
00:00 TRACE: [kryo] Register class ID 7: long (com.esotericsoftware.kryo.serializers.DefaultSerializers$LongSerializer)
00:00 TRACE: [kryo] Register class ID 8: double (com.esotericsoftware.kryo.serializers.DefaultSerializers$DoubleSerializer)
00:00 TRACE: [kryo] Register class ID 9: void (com.esotericsoftware.kryo.serializers.DefaultSerializers$VoidSerializer)

能夠看到, 他把java內置的一些類型,註冊了一個 ID, 這個ID標識具體的 字段採用什麼方法序列化,能夠減小序列化之後的大小。github

具體能夠看下面的過程:數組

00:00 TRACE: [kryo] Optimize ints: true
00:00 TRACE: [kryo] Field id: int
00:00 TRACE: [kryo] Field name: class java.lang.String
00:00 TRACE: [kryo] Field password: class java.lang.String
00:00 TRACE: [kryo] Field orders: interface java.util.List
00:00 TRACE: [kryo] Register class name: User (com.esotericsoftware.kryo.serializers.FieldSerializer)
00:00 TRACE: [kryo] FieldSerializer.write fields of class: User
00:00 TRACE: [kryo] Write field: name (User) pos=2
00:00 TRACE: [kryo] Write initial object reference 1: test
00:00 TRACE: [kryo] Write: test
00:00 TRACE: [kryo] Write field: orders (User) pos=7
00:00 TRACE: [kryo] Register class name: java.util.ArrayList (com.esotericsoftware.kryo.serializers.CollectionSerializer)
00:00 TRACE: [kryo] Write class name: java.util.ArrayList
00:00 TRACE: [kryo] Write initial object reference 2: [test, test, p]
00:00 DEBUG: [kryo] Write: [test, test, p]
00:00 TRACE: [kryo] Write class 1: String
00:00 DEBUG: [kryo] Write object reference 1: test
00:00 TRACE: [kryo] Write class 1: String
00:00 DEBUG: [kryo] Write object reference 1: test
00:00 TRACE: [kryo] Write class 1: String
00:00 TRACE: [kryo] Write initial object reference 3: p
00:00 TRACE: [kryo] Write: p
00:00 TRACE: [kryo] Write field: password (User) pos=38
00:00 DEBUG: [kryo] Write object reference 1: test
00:00 TRACE: [kryo] Object graph complete.

對於咱們一些自定義的字段,咱們也能夠調用 kroy 的方法,主動註冊:安全

com.esotericsoftware.kryo.Kryo#register(java.lang.Class, int)

主動編碼一個ID出來。多線程

** 說明:**併發

  • 這裏的ID不能重複,
  • 若是序列化 和反序列化不是用的同一個 kryo實例,這裏的 ID必須都要調用register方法註冊相同的ID。例如RPC調用中, 不然會出現反序列化出錯的狀況。

有關 writeObject , writeClassAndObject , readObject , readClassAndObject

  • writeObject 和 readObject 屬於一對關係,採用 writeObject 序列化, 則必須 採用 readObject 反序列化。框架

  • writeClassAndObject 和 readClassAndObject 如此。性能

** 兩個的區別: **ui

  • 在反序列化的時候, readObject 須要知道 序列化的具體的類的類型, readClassAndObject 不須要。
public <T> T readObject (Input input, Class<T> type) 

      	public Object readClassAndObject (Input input)
  • 序列化之後的大小不同:

因爲readClassAndObject 在反序列化的時候,不須要知道具體的實體的類型 , 因此 writeClassAndObject 序列化的時候,將對於的類的類的名字,添加到了序列化的流的頭部。因此, writeClassAndObject之後的字符流要比readObject大一些。

咱們將序列化之後的byte[]打印出來以下: 因爲是採用字符串打印,因此,有亂碼正常。 writeClassAndObject 結果:

bytes.length 54
        字符串 :  com.test.Use�tes�java.util.ArrayLis��p

writeObject 結果:

bytes.length 39
      字符串 : tes� java.util.ArrayLis��p

bytes數組的大小,一個是 54 , 一個是 39 , 差異仍是比較大。

有關 references :

能夠理解爲引用,我舉一個例子能夠說明:

有一個以下的實例:

class User {
           String name , 
           String password ,

            ……
    }
咱們在實例化的時候以下:  new User("name" , "name") ,  name  和 password 賦值相同,則,kroy在序列化的時候,第二個字段會採用相似指針的形式,指向對一個 name的值。

    能夠減小序列化之後的字符流的大小。

有關併發的說明:

kroy實例不是線程安全的,若是是多線程訪問,建議採用線程池的形式:
KryoFactory factory = new KryoFactory() {
  public Kryo create () {
    Kryo kryo = new Kryo();
    // configure kryo instance, customize settings
    return kryo;
  }
};
// Build pool with SoftReferences enabled (optional)
KryoPool pool = new KryoPool.Builder(factory).softReferences().build();
Kryo kryo = pool.borrow();
// do s.th. with kryo here, and afterwards release it
pool.release(kryo);

總結

瞭解這些,能夠大概對kroy有個整體的認識和理解 。 更深刻的認識仍是建議參考官方文檔。

相關文章
相關標籤/搜索