本文主要梳理Kryo序列化基本实现。重点剖析Kryo#writeClassAndObject、Kryo#readClassAndObject方法。
kryo对象序列化入口为Kryo的writeClassAndObject。
Kryo#writeClassAndObject
1public void writeClassAndObject (Output output, Object object) {
2 if (output == null) throw new IllegalArgumentException("output cannot be null.");
3 beginObject(); // @1
4 try {
5 if (object == null) {
6 writeClass(output, null); // @2
7 return;
8 }
9 Registration registration = writeClass(output, object.getClass()); // @3
10 if (references && writeReferenceOrNull(output, object, false)) { // @4
11 registration.getSerializer().setGenerics(this, null);
12 return;
13 }
14 if (TRACE || (DEBUG && depth == 1)) log("Write", object);
15 registration.getSerializer().write(this, output, object); // @5
16 } finally {
17 if (--depth == 0 && autoReset) reset(); // @6
18 }
19}
1/** @param object May be null if mayBeNull is true.
2 * @return true if no bytes need to be written for the object. */
3 boolean writeReferenceOrNull (Output output, Object object, boolean mayBeNull) { // @1
4 if (object == null) { // @2
5 if (TRACE || (DEBUG && depth == 1)) log("Write", null);
6 output.writeVarInt(Kryo.NULL, true);
7 return true;
8 }
9 if (!referenceResolver.useReferences(object.getClass())) { // @3
10 if (mayBeNull) output.writeVarInt(Kryo.NOT_NULL, true);
11 return false;
12 }
13
14 // Determine if this object has already been seen in this object graph.
15 int id = referenceResolver.getWrittenId(object); // @4
16
17 // If not the first time encountered, only write reference ID.
18 if (id != -1) { // @5
19 if (DEBUG) debug("kryo", "Write object reference " + id + ": " + string(object));
20 output.writeVarInt(id + 2, true); // + 2 because 0 and 1 are used for NULL and NOT_NULL.
21 return true;
22 }
23
24 // Otherwise write NOT_NULL and then the object bytes.
25 id = referenceResolver.addWrittenObject(object); // @6
26 output.writeVarInt(NOT_NULL, true);
27 if (TRACE) trace("kryo", "Write initial object reference " + id + ": " + string(object));
28 return false; // @7
29 }
1public int getWrittenId (Object object) {
2 for (int i = 0, n = seenObjects.size(); i < n; i++) {
3 if (seenObjects.get(i) == object) {
4 return i;
5 }
6 }
7 return -1;
8}
Kryo#writeClassAndObject 代码@5
1registration.getSerializer().write(this, output, object); // @5
其实其重点关键,还是writeClassAndObject#writeClass也就是上文说的代码@3,在序列化对象之前,首先先序列化该对象的类型,然后需要返回对应的字段序列器。例如,如果类的类型为java.util.Map,则首先先要记录类型为Map,然后返回可以序列化Map的序列器,再例如类型如果是java.lang.String,则先序列化类型,然后序列化值,序列化值的序列器则为StringSerializer,那如果是一个对象类型,例如cn.uce.demo.Student,自然第一步是先序列化类型cn.uce.demo.Student,接下来就需要序列化Student的各个字段的信息,返回的序列化为FieldSerializer,然后可以通过FieldSeriaizer返回Student的属性列表,然后单独一个字段一个字段的序列化,其顺序也就是,先类型,再序列化值。这样就递归完成了一个对象的序列化操作。
1、先序列化类型(Class实例),然后根据类型返回相应的序列化器(上一篇详细介绍了各种类型的序列化器)。
2、再序列化该类型的值。
3、如果自定义类型,例如(cn.uce.demo.Student),则返回的值序列化器为DefaultSerializers$FieldSerializer,然后一个字段一个字段的序列化,当然其序列化类型也是,先类型再值的模式,递归进行,最终完成。
4、引入了对象图的概念来消除循环依懒的序列化,已序列化的对象,在循环引用时,只是用一个int类型来表示该对象值,类似一种缓存的概念。
kryo的设计目的是指对象值的序列化,关注的是有效数据的传输,减少需要序列化的元数据信息。
这一点通过Kryo对Class对象的序列化,也就是类型的序列化就能看出端倪。Kryo对Class的序列化只需要化Class的全路径名,在反序列化时根据Class通过类加载进行加载,大大减少了序列化后的文件大小,能极大提高性能。
Kryo的核心设计理念就是尽最大可能减少序列化后的文件大小,其举措1就是通过对long,int等数据类型,采用变长字节存储来代替java中使用固定字节(4,8)字节的模式,因为在软件开发中,对象的这些值基本上都是小值,能节省很多空间,第二个举措是使用了类似缓存的机制,在一次序列化对象中,在整个递归序列化期间,相同的对象,只会序列化一次,后续的用一个局部int值来代替。