引言
色弱 目前移动客户端应用程序上,需要将用户内容持久化到设备上,一般任何feed流应用,如微博、推特、新闻客户端等都需要将内容做持久化操作,以便在内存回收后,再次进入程序能迅速恢复之前的内容。另外如一些视频、音乐、购物等软件,凡是收藏的视频、歌曲、商品以及个人主页等,也应将这些用户私有的内容做序列化,以便无网进入时也能看到相关内容,并正常使用软件。
陆陆续续使用和测试过一些Java序列化方案,这篇主要从Android客户端应用程序的角度,并以速度、序列化文件大小、实践简易性为主要考虑指标介绍并对比以下序列化方案。
JVM-Serializsers
首先介绍一下JVM-Serializsers,是一个很不错的测试序列化的工具,可以用它来评测各种流行的java序列化反序列化工具,在其测试模型上构建新的序列化使用上也很方便。本文结合JVM-Serializsers测试模型,并在其测试模型下新增测试了fast-serialization的测试。本文重点介绍Java原生序列化、Kryo、fast-serialiation、fastjson、protocol-buffers等几种典型的序列化方案。
JVM-Serializsers下载源码后,根据readme所示:
Requirements:
- GNU Make 3.81+
- JDK 1.5+
To compile:
make
To run:
./run -help
具体步骤:
1.切到tcp源码目录下
2.编译源码:
3.运行测试案案例:
1 | ./run -chart -include=kryo,fst-serialization,java-built-in,protobuf,hessian,json/google-gson/databind,xml/xstream+c,bson/mongodb,bson/jackson/databind,json/fastjson/databind,json/jackson/databind,thrift,avro-generic data/media.1.cks |
include参数代表要进行测试的序列化工具,当然前提是在jvm-serialization中的测试模型下已经构建完相应工具的测试用例。更多参数可通过./run -help看说明,如果带上-chart参数,还会生成序列化性能数据的图形对比。
4.运行结果:
表中参数介绍:
-
Total Time (“total”):创建一个对象,将其序列化成一个字节数组,然后再反序列化成一个对象。
-
Serialization Time (“ser”):创建一个对象,将其序列化成一个字节数组。
-
Deserialization Time (“deser+deep”):相比于序列化,反序列化更耗时。为了更公平的比较,jvm-serializers在反序列化测试时访问了反序列化得到的对象的所有字段(也就是deep的含义),因为部分工具反序列化时“偷懒”而没有做足工作。
-
Serialized Size (“size”):序列化数据的大小,这个大小会依赖于使用的数据。
-
Serialization Compressed Size (“size+dfl”):使用java内置的DEFLATE(zlib)压缩的序列化数据的大小。
-
Object Creation Time (“create”):对象创建耗时很短(平均100纳秒)所以通常的比较没什么意义。不过,不同工具创建的对象在表现上会有不同。有的工具只是创建普通的java类,你可以直接访问其字段,而有的使用get/set方法,有的使用builder模式。
该工具还将这些数据通过google chart服务生成数据图形对比图:
图2-1
5.测试环境
os:os x-10.9
jdk:java version "1.7.0_51"
mem:16G
cpu: 2.3 GHz Intel Core i7
更多在Android Runtime的测试见下文。
以上,已经可以利用强大的JVM-Serializsers工具来分析跟构建自己想测试的序列化工具了。
Java序列化工具技术原理比较
-
Binary Formats & language-specific ones
JavaBuiltIn(java原生)、JavaManual(根据成员变量类型,手工写)、FstSerliazation、Kryo
-
Binary formats-generic language-unspecific ones
Protobuf、Thrift、 AvroGeneric、Hessian
-
JSON Format
-
JSON-like:
CKS (textual JSON-like format)、BSON(JSON-like format with extended datatypes)
JacksonBson、MongoDB
-
XML-based formats
java的序列化工具大致就可以分为以上几类,简单概括就分为二进制binary和文本格式(json、xml)两大类。
从图2-1中可以较为明显的看出,在速度的对比上一般有如下规律:
binary > textual
language-specific > language-unspecific
而textual中,由json相比xml冗余度更低因此速度上更胜一筹,而json又bson这类textual serialization技术上更成熟,框架的选择上更丰富和优秀。下面重点介绍下Kryo、fast-serialiation、fastjson、protocol-buffer
典型Java序列化工具分析
Java原生序列化工具
Java本身提供的序列化工具基本上能胜任大多数场景下的序列化任务,关于其序列化机制,这篇文章很细致的解释了,值得一读。Java自带的序列化工具在序列化过程中需要不仅需要将对象的完整的class name记录下来,还需要把该类的定义也都记录下,包括所有其他引用的类,这会是一笔很大的开销,尤其是仅仅序列化单个对象的时候。正因为java序列化机制会把所有meta-data记录下来,因此当修改了类的所在的包名后,反序列化则会报错。Java自带序列化工具的性能问题总结如下:
-
一个single object的序列化会递归地,连同所有成员变量(instsnce variables)一起序列化了,这种默认机制很容易造成不必要的序列化开销。
-
序列化和反序列化过程需要上面的这种机制去递归并用反射机制去寻找所有成员变量的信息,另外如果没定义自己serialVersionUID的话,那么对象及其他变量都必须自己产生一个。上述过程开销很大。
-
使用默认序列化机制,所有序列化类定义完整信息都会被记录下来,包括所有包名、父类信息、以及成员变量
优化过的Java序列化工具
kryo
kryo根据上述Java原生序列化机制的一些问题,对了很多优化工作
01 | private static Kryo myKryo = new Kryo(); |
02 |
03 | static { |
04 | myKryo.register(MusicInfo. class , new CompatibleFieldSerializer<MusicInfo>(myKryo, Media. class ), 50 ); |
05 | } |
06 | |
07 | public static void saveObjectByKryo(Object o, String fileName) { |
08 | Output output = null ; |
09 | try { |
10 | output = new Output( new FileOutputStream(fileName), 8 * 1024 ); |
11 | myKryo.writeObject(output, o); |
12 | } catch (IOException e) { |
13 | e.printStackTrace(); |
14 | } finally { |
15 | if (output != null ) { |
16 | output.close(); |
17 | } |
18 | } |
19 | } |
20 |
21 | public static Object readObjectByKryo(String filename, Class<?> clazz) { |
22 | Input input = null ; |
23 | try { |
24 | input = new Input( new FileInputStream( new File(filename))); |
25 | return myKryo.readObject(input, clazz); |
26 | } catch (Throwable t) { |
27 | t.printStackTrace(); |
28 | } finally { |
29 | if (input != null ) { |
30 | input.close(); |
31 | } |
32 | } |
33 | return null ; |
34 | } |
JSON
从上一节的多种序列化工作的表现来看,比较优秀的JSON解析工具的表现还是比较好的,有些json解析工具甚至速度超过了一些二进制的序列化方式。Android环境下也有评测json解析性能的demo,图3-1,可以看出jackson在速度上还是比较有优势的,但与Android自带的json包,也没有数量级以上的优势,而jackson的jar包大小达1mb多,因此对于普通的android应用来说是比较奢侈的。
参考博客:
Java序列化机制介绍及简单优化方法: http://www.javacodegeeks.com/2010/07/java-best-practices-high-performance.html
Java序列化“最佳”实践: http://www.javacodegeeks.com/2010/07/java-best-practices-high-performance.html
提升Java序列化的几种方法: http://www.javacodegeeks.com/2013/09/speed-up-with-fast-java-and-file-serialization.html
详细的Java序列化过程: http://blog.csdn.net/zhaozheng7758/article/details/7820018