Java 序列化综述-由《Efficient Java serialization》说起

来源:互联网 发布:淘宝店铺售后服务内容 编辑:程序博客网 时间:2024/05/21 15:50

Java 序列化综述-由《Efficient Java serialization》说起

Hazelcast 是一个提供分布式缓存、分布式锁、分布式线程池功能的In-Memory Data Grid的开源实现。Hazelcast数据分布式传输的序列化解决方案使用kyro方式来减少数据序列化占用的空间和消耗的时间,同时减少网络传输压力。根据《Efficient Java serializationhttp://open.bekk.no/efficient-java-serialization的介绍,只要实现了java.io.Serializable/Externalizable或者com.hazelcast.nio.DataSerializable接口,数据就能在hazelcast网络中传输,但是为了兼容已经存在的数据类代码,仅仅让这个遗留类实现Serializable接口还是不够的,因为java原生序列化机制在时间、空间效率上表现很差;如果实现Externalizable接口,就需要编写自定义的序列化代码,代价比较大;因此根据jvm-serializersbenchmark结果,选择kryo来解决遗留数据类的序列化问题。 但是Hazelcast2.0.2)不支持自定义的序列化编码,开发人员通过wrapper方式来绕过这个限制,而且通过这种方式不要求遗留数据类做任何修改,不需要implements Serializable/Externalizable接口--Decorator设计模式,符合设计模式中所说的开闭原则。

public class KryoWrapper implements Serializable {

    private byte[] s; 

   public KryoWrapper(Object target) {

      s = KryoSerializer.write(target);

   }

 

   private Object readResolve() throws ObjectStreamException {

      return KryoSerializer.read(s);

   }

}

注意:KryoWrapper的序列化方式是jvm serializable机制,KryoWrapper的原理就是通过jvm serializablejvm无缝结合,同时内部使用Kryo来尽量提高数据的序列化效率。

二、JVM Serializable

对于实现Serializable接口的类,jvm序列化和反序列化的流程:

ObjectOutputStream采用默认的序列化方式对实例的非transient实例变量、非静态变量进行序列化--ObjectOutputStream.defaultWriteObject()ObjectInputStream.defaultReadObject()完成这些逻辑。

ObjectInputStream采用默认的反序列化方式对实例的非transient实例变量、非静态变量进行反序列化。如果对象所属的类没有在jvm中加载,那么首先加载并初始化这个类,如果classpath中不存在这个类,则抛出ClassNotFoundException。同时反序列化的时候不会调用类的任何构造方法。

如果实例自定义了writeObject(ObjectOutputStream out)readObject(ObjectInputStream in)方法,那么则默认调用这些方法进行序列化和反序列化。

注:writeObjectreadObject必须是private void类型的,优点:更好的封装序列化的细节;一般是成对出现;是可选的实现。

自定义writeObjectreadObject的好处:

1、有些transient修饰敏感的信息在传输中需要加密,加解密的逻辑在这2个方法中完成。

2、可以在真正的序列化之前进行数据校验,然后调用ObjectOutputStream.defaultWriteObjectObjectInputStream.defaultReadObject方法进行序列化和反序列化

3、优化某些数据结构序列化效率。例如:对自定义实现的LinkedList,默认的序列化方式不仅需要序列化每个节点的value,还要序列化这些节点的前后节点。实际上只需要将每个节点的value序列化,然后特定的逻辑重新构建这个双向链表就行了。也就是用代码逻辑来换空间和时间。

4、避免接口的序列化不会被特定的数据结构束缚。例如,某个实例的数据结构由LinkedList转换为ArrayList了,通过 3的自定义序列方式,这种数据结构的转换不会有任何影响。其实对于集合的序列化,只要使用Iterator模式,同一种接口的数据类型的转变是不会有任何影响的。

对于实现Externalizable接口的类,jvm序列化和反序列化的流程:

ObjectOutputStream调用实例writeExternal(ObjectOut out)方法来进行序列化。和writeObject(ObjectOutputStream out)功能一样,区别在于writeExternal是接口方法,public类型的方法

ObjectInputStream首先通过类的不带参数的构造方法创建一个实例,然后调用实例的readExternal(ObjectInput in)方法进行反序列化。和readObject区别同上。

三、writeReplace/readResolve

[private/public/protected] Object readResolve()/writeReplace()是在反序列化完成之后/序列化开始之前 执行,可以当做序列化的aop

[private/public/protected] Object writeReplace() 方法在writeObject/defaultWriteObject/writeExternal方法执行之前被调用,主要是用来返回一个实例来代替当前实例进行序列化操作。也可以对当前实例的属性做修改,例如敏感信息的加密。

[private/public/protected] Object readResolve() 方法在readObject/defaultReadObject/readExternal方法执行之后才执行,用来返回一个实例来代替被方法反序列化的实例。这个方法可以用来单例模式对象的序列化。或者对当前实例的变量做修改、有效性校验。在Efficient Java serialization》中,这个方法主要完成实例的转换作用。

四、序列化数据格式

在这个地址有详细的介绍:java序列化机制和原理 http://www.java3z.com/cwbwebhome/article/article8/862.html

在测试中,一个类中只有基本的数据对象(没有组合依赖其他类),通过SerializableExternalizable主要区别在Serializable方式序列化会把属性的变量名称、类型放到序列化文件中,如果一个类有和很多自定义属性,就会导致jvm serializable重复数据过多、速度慢、数据占用空间大的问题

这也是IDL类型的序列化方案(典型的是protocol buffer)占用空间小、速度快的原因:变量名称已经在描述文件中存在了,不需要通过反射判断类中的哪些属性需要序列化。

参考:

http://www.ibm.com/developerworks/cn/java/j-lo-serial/index.html

http://www.ibm.com/developerworks/cn/java/j-5things1/index.html

原创粉丝点击