Java序列化工具对比

1. Java序列化工具技术原理比较

  • Binary Formats & language-specific ones

    JavaBuiltIn(java原生)、JavaManual(根据成员变量类型,手工写)、FstSerliazationKryo

  • Binary formats-generic language-unspecific ones

    Protobuf(Google)Thrift(Facebook)、 AvroGeneric、Hessian

  • JSON Format

    Jackson、Gson、FastJSON

  • JSON-like:

    CKS (textual JSON-like format)、BSON(JSON-like format with extended datatypes)、JacksonBson、MongoDB

  • XML-based formats

    XmlXStream

java的序列化工具大致就可以分为以上几类,简单概括就分为二进制binary和文本格式(json、xml)两大类。

在速度的对比上一般有如下规律:

binary > textual

language-specific > language-unspecific

而textual中,由json相比xml冗余度更低因此速度上更胜一筹,而json又bson这类textual serialization技术上更成熟,框架的选择上更丰富和优秀。下面重点介绍下Kryo、fast-serialiation、fastjson、protocol-buffer

2. 典型Java序列化工具分析

目前互联网公司广泛使用Protobuf、Thrift、Avro等成熟的序列化解决方案来搭建RPC框架,这些都是久经考验的解决方案。

2.1 Java原生序列化工具

Java本身提供的序列化工具基本上能胜任大多数场景下的序列化任务,关于其序列化机制,这篇文章很细致的解释了(https://blog.csdn.net/zhaozheng7758/article/details/7820018),值得一读。Java自带的序列化工具在序列化过程中需要不仅需要将对象的完整的class name记录下来,还需要把该类的定义也都记录下,包括所有其他引用的类,这会是一笔很大的开销,尤其是仅仅序列化单个对象的时候。正因为java序列化机制会把所有meta-data记录下来,因此当修改了类的所在的包名后,反序列化则会报错。Java自带序列化工具的性能问题总结如下:

一个single object的序列化会 递归地,连同所有成员变量(instsnce variables)一起序列化了,这种默认机制很容易造成不必要的序列化开销。

序列化和反序列化过程需要上面的这种机制去递归并用反射机制去寻找所有成员变量的信息,另外如果没定义自己serialVersionUID的话,那么对象及其他变量都必须自己产生一个。上述过程开销很大。

使用默认序列化机制,所有序列化类定义完整信息都会被记录下来,包括所有包名、父类信息、以及成员变量

2.2 优化过的Java序列化工具

  1. kryo

    kryo根据上述Java原生序列化机制的一些问题,对了很多优化工作,而且提供了很多serializer,甚至封装了Unsafe类型的序列化方式,更多关于Unsafe类型的序列化方式,请参考这里,需要注意的是,jdk1.7以后,默认关闭unsafe的类(sun.misc.Unsafe)包。更多kryo介绍参考kryo的wiki.

  2. fast-serialization

    fst-serialozation相对来说是一个很新的序列化工具,虽然从2-1的评测上来看,速度于kryo有一些差距,但根据本人在生产环境上的场景上测试,效果几乎于kryo一致,都能瞬间反序列化出内容并渲染

2.3 JSON

比较优秀的JSON解析工具的表现还是比较好的,有些json解析工具甚至速度超过了一些二进制的序列化方式。

2.4 Protocol-Buffer

Protocol buffers是一个用来序列化结构化数据的技术,支持多种语言诸如C++、Java以及Python语言,可以使用该技术来持久化数据或者序列化成网络传输的数据。相比较一些其他的XML技术而言,该技术的一个明显特点就是更加节省空间(以二进制流存储)、速度更快以及更加灵活。

另外Protobuf支持的数据类型相对较少,不支持常量类型。由于其设计的理念是纯粹的展现层协议(Presentation Layer),目前并没有一个专门支持Protobuf的RPC框架。

2.5 Thrift

Thrift是Facebook开源提供的一个高性能,轻量级RPC服务框架,其产生正是为了满足当前大数据量、分布式、跨语言、跨平台数据通讯的需求。 但是,Thrift并不仅仅是序列化协议,而是一个RPC框架。 相对于JSON和XML而言,Thrift在空间开销和解析性能上有了比较大的提升,对于对性能要求比较高的分布式系统,它是一个优秀的RPC解决方案;但是由于Thrift的序列化被嵌入到Thrift框架里面, Thrift框架本身并没有透出序列化和反序列化接口,这导致其很难和其他传输层协议共同使用(例如HTTP)。

2.6 Avro

Avro解析性能高并且序列化之后的数据非常简洁,比较适合于高性能的序列化服务。

Avro提供两种序列化格式:JSON格式或者Binary格式。Binary格式在空间开销和解析性能方面可以和Protobuf媲美, JSON格式方便测试阶段的调试。 Avro支持的数据类型非常丰富,包括C++语言里面的union类型。Avro支持JSON格式的IDL和类似于Thrift和Protobuf的IDL(实验阶段),这两者之间可以互转。Schema可以在传输数据的同时发送,加上JSON的自我描述属性,这使得Avro非常适合动态类型语言。 Avro在做文件持久化的时候,一般会和Schema一起存储,所以Avro序列化文件自身具有自我描述属性,所以非常适合于做Hive、Pig和MapReduce的持久化数据格式。对于不同版本的Schema,在进行RPC调用的时候,服务端和客户端可以在握手阶段对Schema进行互相确认,大大提高了最终的数据解析速度。

3.下面介绍几种常用的Java序列化技术使用示例

KryoRegister、FST、Kryo、Gson、Fastjson、JDK

3.1 JDK

public static byte[] serialize(Object obj) {
    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        byte[] bs = baos.toByteArray();
        baos.close();
        oos.close();

        return bs;
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

public static Object deserialize(byte[] bits) {
    try {
        ByteArrayInputStream bais = new ByteArrayInputStream(bits);
        ObjectInputStream ois = new ObjectInputStream(bais);
        Object obj = ois.readObject();

        bais.close();
        ois.close();
        return obj;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}  

3.2 Fastjson

一个JSON库涉及的最基本功能就是序列化和反序列化。Fastjson支持java bean的直接序列化。 使用com.alibaba.fastjson.JSON这个类进行序列化和反序列化。

public static String serialize(Object obj){
    String json = JSON.toJSONString(obj);
    return json;
}
public static Object deserialize(String json, Class<?> clazz){
    Object obj = JSON.parseObject(json, clazz);
    return obj;
}

3.3 FST

FST fast-serialization 是重新实现的 Java 快速对象序列化的开发包。序列化速度更快(2-10倍)、体积更小,而且兼容 JDK 原生的序列化。

Java 快速序列化库 FST 已经发布了 2.0 版本,该版本的包名已经更改,无法平滑升级。另外官方建议为了稳定性考虑还是使用最新的 1.58 版本为好

static FSTConfiguration configuration = FSTConfiguration
           .createDefaultConfiguration();

public static byte[] serialize(Object obj){
     return configuration.asByteArray((Serializable)obj);
}
public static Object deserialize(byte[] sec){
    return configuration.asObject(sec);
}

3.4 Gson

这里采用JSON格式同时使用采用Google的gson进行转义.

static Gson gson = new Gson();

public static String serialize(Object obj){
    String json = gson.toJson(obj);
    return json;
}
public static Object deserialize(String json, Class<?> clazz){
    Object obj = gson.fromJson(json, clazz);
    return obj;
}

3.5 Jackson

Jackson库(http://jackson.codehaus.org),是基于java语言的开源json格式解析工具,整个库(使用最新的2.2版本)包含3个jar包:

jackson-core.jar——核心包(必须),提供基于“流模式”解析的API。

jackson-databind——数据绑定包(可选),提供基于“对象绑定”和“树模型”相关API。

jackson-annotations——注解包(可选),提供注解功能。

相对于java json解析的其他库,诸如json-lib、gson包,Jackson具有以下优点:

功能全面,提供多种模式的json解析方式,“对象绑定”使用方便,利用注解包能为我们开发提供很多便利。

性能较高,“流模式”的解析效率超过绝大多数类似的json包。

核心包:JsonPaser(json流读取),JsonGenerator(json流输出)。

数据绑定包:ObjectMapper(构建树模式和对象绑定模式),JsonNode(树节点)

public static String serialize(Object obj){
    ObjectMapper mapper = new ObjectMapper();
    String json = null;
    try {
        json = mapper.writeValueAsString(obj);
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return json;
}
public static Object deserialize(String json, Class<?> clazz){
    ObjectMapper mapper = new ObjectMapper();
    Object obj = null;
    try {
        obj = mapper.readValue(json, clazz);
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return obj;
}

3.6 Kryo 和 KryoRegister

Kryo的运行速度是java Serializable 的20倍左右

Kryo的文件大小是java Serializable的一半左右

Kryo有两种模式:

一种是先注册(regist),再写对象,即writeObject函数,实际上如果不先注册,在写对象时也会注册,并为class分配一个id。

注意,如果是rpc,则必须两端都按同样的顺序注册,否则会出错,因为必须要明确类对应的唯一id。

另一种是写类名及对象,即writeClassAndObject函数。

writeClassAndObject函数是先写入(-1 + 2)(一个约定的数字),再写入类ID(第一次要先写-1,再写类ID + 类名),写入引用关系(见引用的实现),最后才写真正的数据)。

注意每一次writeClassAndObject调用后信息都会清空,所以不用担心和client交互时会出错。

static Kryo kryo = new Kryo();

public static byte[] serialize(Object obj) {
    byte[] buffer = new byte[2048];
    Output output = new Output(buffer);

    kryo.writeClassAndObject(output, obj);
    byte[] bs = output.toBytes();
    output.close();
    return bs;
}

public static Object deserialize(byte[] src) {
    Input input = new Input(src);
    Object obj = kryo.readClassAndObject(input);
    input.close();
    return obj;
}

register

static Kryo kryo = null;
static{
    kryo = new Kryo();
    kryo.setReferences(false);
    kryo.setRegistrationRequired(false);
    kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
}

public static byte[] serialize(Object obj) {
    kryo.register(obj.getClass());
    byte[] buffer = new byte[2048];
    Output output = new Output(buffer);
    kryo.writeObject(output, obj);
    byte[] bs = output.toBytes();
    output.close();
    return bs;
}

public static Object deserialize(byte[] src, Class<?> clazz) {
    kryo.register(clazz);
    Input input = new Input(src);
    Object obj = kryo.readObject(input, clazz);
    input.close();
    return obj;
}

Object Serializalbe 优点:java原生支持,不需要提供第三方的类库,使用比较简单。缺点:无法跨语言,字节数占用比较大,某些情况下对于对象属性的变化比较敏感。

对象在进行序列化和反序列化的时候,必须实现Serializable接口,但并不强制声明唯一的serialVersionUID,是否声明serialVersionUID对于对象序列化的向上向下的兼容性有很大的影响。

4. 小结

就已有原先使用Java原生序列化方案的系统来说,kryo于fst-serializer是良好的java原生序列化方案替代者,不仅体现再编程简单,而且速度与性能上会有大幅提升,尤其是fst-serializer ,只需替代output/inputstream 即可,性能的提升上也很可观,目前该工具刚出来,稳定性还需要多测测。

如果程序本身就用json格式序列化,则可以考虑引入一个性能优异的json解析库,一般再服务端jackson是广受欢迎的解析库。

protobuffer更多的是一种取代xml的夸语言的消息交换格式,尽快速度很快,但是编程上需要定义消息格式,对成员变量多、业务复杂的javabean来说代价是较为复杂的,对稳定的已有系统来说总体代价较高。

下表是几种方案的各项指标的一个对比

序列化工具 序列化速度 序列化文件大小 编程模型复杂度 社区活跃度 jar包大小
kryo 极快 简单 132kb
fst-serializer 非常简单 246kb
protobuffer 较大 较复杂 稳定 329kb
fastjson 较快 较大 简单 一般 338kb
jackson 一般 较大 简单 稳定 1.1mb
gson 较慢 较大 简单 稳定 189kb

参考:

http://blog.51cto.com/zlfwmm/1761401

https://blog.csdn.net/wodeyuer125/article/details/44495549

https://www.javacodegeeks.com/2010/07/java-best-practices-high-performance.html

http://www.javacodegeeks.com/2010/07/java-best-practices-high-performance.html

https://blog.csdn.net/zhaozheng7758/article/details/7820018

http://www.javacodegeeks.com/2012/07/native-cc-like-performance-for-java.html

https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/

原文地址:https://www.cnblogs.com/john8169/p/9261508.html

时间: 2024-10-10 20:16:59

Java序列化工具对比的相关文章

针对工作中的需要对已有的Java序列化工具分析

针对java原生序列化,以及优化过的java序列化工具列举说明.自己定义的类名(IOSerializable).(Fast Serialization).(FastjsonSerializable) 1.java序列化简介 序列化就是指对象通过写出描述自己状态的数值来记录自己的过程,即将对象表示成一系列有序字节,Java提供了将对象写入流和从流中恢复对象的方法.对象能包含其它的对象,而其它的对象又可以包含另外的对象.JAVA序列化能够自动的处理嵌套的对象.对于一个对象的简单域,writeObje

浅析若干Java序列化工具

??在java中socket传输数据时,数据类型往往比较难选择.可能要考虑带宽.跨语言.版本的兼容等问题.比较常见的做法有: 采用java对象的序列化和反序列化 把对象包装成JSON字符串传输 Google工具protoBuf的开源 本文章所需要的序列化jar包都可以下载:http://download.csdn.net/detail/u013256816/9439971. ??为了便于说明各个做法的区别,分别对这三种做法进行阐述. 对UserVo对象进行序列化,class UserVo如下:

Java 序列化工具类

import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputSt

[java]序列化框架性能对比(kryo、hessian、java、protostuff)

序列化框架性能对比(kryo.hessian.java.protostuff) 简介:   优点 缺点 Kryo 速度快,序列化后体积小 跨语言支持较复杂 Hessian 默认支持跨语言 较慢 Protostuff 速度快,基于protobuf 需静态编译 Protostuff-Runtime 无需静态编译,但序列化前需预先传入schema 不支持无默认构造函数的类,反序列化时需用户自己初始化序列化后的对象,其只负责将该对象进行赋值 Java 使用方便,可序列化所有类 速度慢,占空间      

Java 序列化与反序列化

1.什么是序列化?为什么要序列化? Java 序列化就是指将对象转换为字节序列的过程,而反序列化则是只将字节序列转换成目标对象的过程. 我们都知道,在进行浏览器访问的时候,我们看到的文本.图片.音频.视频等都是通过二进制序列进行传输的,那么如果我们需要将Java对象进行传输的时候,是不是也应该先将对象进行序列化?答案是肯定的,我们需要先将Java对象进行序列化,然后通过网络,IO进行传输,当到达目的地之后,再进行反序列化获取到我们想要的对象,最后完成通信. 2.如何实现序列化 2.1.使用到JD

Java序列化的几种方式以及序列化的作用

Java序列化的几种方式以及序列化的作用 本文着重讲解一下Java序列化的相关内容. 如果对Java序列化感兴趣的同学可以研究一下. 一.Java序列化的作用    有的时候我们想要把一个Java对象变成字节流的形式传出去,有的时候我们想要从一个字节流中恢复一个Java对象.例如,有的时候我们想要 把一个Java对象写入到硬盘或者传输到网路上面的其它计算机,这时我们就需要自己去通过java把相应的对象写成转换成字节流.对于这种通用 的操作,我们为什么不使用统一的格式呢?没错,这里就出现了java

Java序列化(Serializable)与反序列化

序列化是干什么的 简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来.虽然你可以用你自己的各种各样的方法来保 存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化. 什么情况下需要序列化 当你想把的内存中的对象状态保存到一个文件中或者数据库中时候: 当你想用套接字在网络上传送对象的时候: 当你想通过RMI传输对象的时候: 序列化的几种方式 在Java中socket传输数据时,数据类型往往比较难

Java序列化、反序列化

序列化是干什么的 简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来.虽然你可以用你自己的各种各样的方法来保 存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化. 什么情况下需要序列化 当你想把的内存中的对象状态保存到一个文件中或者数据库中时候: 当你想用套接字在网络上传送对象的时候: 当你想通过RMI传输对象的时候: 序列化的几种方式 在Java中socket传输数据时,数据类型往往比较难

【Java】Java 序列化的高级认识

如果你只知道实现 Serializable 接口的对象,可以序列化为本地文件.那你最好再阅读该篇文章,文章对序列化进行了更深一步的讨论,用实际的例子代码讲述了序列化的高级认识,包括父类序列化的问题.静态变量问题.transient 关键字的影响.序列化 ID 问题.在笔者实际开发过程中,就多次遇到序列化的问题,在该文章中也会与读者分享. 15 评论: 杨 硕, 研究生, 东北大学软件学院 魏 强, 硕士研究生, 东北大学软件学院 2011 年 3 月 16 日 内容 引言 将 Java 对象序列