一些常用Java序列化框架的比较
概念
序列化:将Java对象转化为字节数组
反序列化:将字节数组转化为Java对象
在RPC应用中,进行跨进程远程调用的时候,需要使用特定的序列化技术,需要对进行网络传输的对象进行序列化和反序列化。
影响序列化选择有两个因素
1. 序列化之后码流的大小,如果太大,那么将会影响网络传输的性能。
2. 序列化和反序列化过程的性能
常用的序列化框架性能比较

本文主要进行以下序列化框架的对比测试:
- JDK
- FastJson
- Hessian
- Protostuff
准备
需要序列化的对象,这是一个复杂的对象。
NettyMessage
public class NettyMessage implements Serializable {
//消息头
private Header header;
//消息体
private Object body;
}
@Data
public class Header implements Serializable {
//校验头
private int crcCode;
//消息头消息体的总长度
private int length;
//全局唯一id
private long sessionId;
//消息类型
private MessageType type;
//扩展字段
private Map<String,Object> attachment;
}
@Data
public class RpcRequest implements Serializable {
private long requestId; //请求id
private String interfaceName; //调用类名
private String methodName; //调用方法名
private String[] parameterTypes; //方法参数类型
private Object[] parameters; //方法参数
}
创建一个构造器创建该对象。
public class NettyMessageBuilder {
public static NettyMessage build(){
NettyMessage message = new NettyMessage();
Header header = new Header();
RpcRequest request = new RpcRequest();
header.setCrcCode(1234);
header.setType(MessageType.APP_RESPONE_TYPE);
header.setLength(100);
header.setSessionId(200);
Map<String,Object> map = new LinkedHashMap<>();
map.put("demoKey",(Object)"demoValue");
header.setAttachment(map);
request.setInterfaceName("com.demo");
String[] types = {"java.lang.String" ,"java.lang.Integer"};
String[] param = {"java.lang.String" ,"java.lang.Integer"};
request.setParameterTypes(types);
request.setParameters(param);
request.setMethodName("buy");
request.setRequestId(123456);
message.setHeader(header);
message.setBody(request);
return message;
}
}
定义序列化接口
public abstract class AbstractSerialize {
public abstract <T> byte[] serialize(T obj);
public abstract <T> T deserialize(byte[] data, Class<T> clazz);
}
JDK
实现
public class JdkSerializeUtil extends AbstractSerialize {
public <T> byte[] serialize(T obj) {
if (obj == null){
throw new NullPointerException();
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
return bos.toByteArray();
} catch (Exception ex) {
ex.printStackTrace();
}
return new byte[0];
}
public <T> T deserialize(byte[] data, Class<T> clazz) {
ByteArrayInputStream bis = new ByteArrayInputStream(data);
try {
ObjectInputStream ois = new ObjectInputStream(bis);
T obj = (T)ois.readObject();
return obj;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
}
FastJson
引入pom
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
实现
public class FastjsonSerializeUtil extends AbstractSerialize {
public <T> byte[] serialize(T obj) {
if (obj == null){
throw new NullPointerException();
}
String json = JSON.toJSONString(obj);
byte[] data = json.getBytes();
return data;
}
public <T> T deserialize(byte[] data, Class<T> clazz) {
T obj = JSON.parseObject(new String(data),clazz);
return obj;
}
}
Hessian
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.60</version>
</dependency>
实现
@Slf4j
public class HessianSerializeUtil extends AbstractSerialize { public <T> byte[] serialize(T obj) { if (obj == null){
throw new NullPointerException();
}
try{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
HessianOutput ho = new HessianOutput(bos);
ho.writeObject(obj); return bos.toByteArray();
}
catch(Exception ex){
log.error("HessianSerializeUtil序列化发生异常!"+ex);
throw new RuntimeException();
} } public <T> T deserialize(byte[] data, Class<T> clazz) { if (data == null){
throw new NullPointerException();
}
try{
ByteArrayInputStream bis = new ByteArrayInputStream(data);
HessianInput hi = new HessianInput(bis);
return (T)hi.readObject(); }
catch(Exception ex){
log.error("HessianSerializeUtil反序列化发生异常!"+ex);
throw new RuntimeException();
} }
}
Protostuff
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>1.6.0</version>
<scope>compile</scope>
</dependency> <!-- https://mvnrepository.com/artifact/io.protostuff/protostuff-runtime -->
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>1.6.0</version>
</dependency>
实现
public class ProtostuffSerializeUtil extends AbstractSerialize {
/**
* 避免每次序列化都重新申请Buffer空间
*/
private static LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
/**
* 缓存Schema
*/
private static Map<Class<?>, Schema<?>> schemaCache = new ConcurrentHashMap<Class<?>, Schema<?>>();
public <T> byte[] serialize(T obj) {
if (obj == null){
throw new NullPointerException();
}
Class<T> clazz = (Class<T>) obj.getClass();
Schema<T> schema = getSchema(clazz);
byte[] data;
try {
data = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
} finally {
buffer.clear();
}
return data;
}
public <T> T deserialize(byte[] data, Class<T> clazz) {
Schema<T> schema = getSchema(clazz);
T obj = schema.newMessage();
ProtostuffIOUtil.mergeFrom(data, obj, schema);
return obj;
}
private static <T> Schema<T> getSchema(Class<T> clazz) {
Schema<T> schema = (Schema<T>) schemaCache.get(clazz);
if (schema == null) {
//这个schema通过RuntimeSchema进行懒创建并缓存
//所以可以一直调用RuntimeSchema.getSchema(),这个方法是线程安全的
schema = RuntimeSchema.getSchema(clazz);
if (schema != null) {
schemaCache.put(clazz, schema);
}
}
return schema;
}
}
测试
测试方法
@Test
public void testFastJsonSerialize(){
//这里替换各种序列化实现类
AbstractSerialize serialize = new ProtostuffSerializeUtil(); NettyMessage message = NettyMessageBuilder.build(); TimeUtil timeUtil = new TimeUtil();
TimeUtil timeUtil1 = new TimeUtil(); NettyMessage result = null;
byte[] serByte = serialize.serialize(message);
System.out.println("字节长度:" + serByte.length);
result = serialize.deserialize(serByte,NettyMessage.class);
//这里设置测试次数
for(int i = 0; i< 100000; i++){
//timeUtil.init();
timeUtil.start();
serByte = serialize.serialize(message);
timeUtil.end();
//System.out.println("序列化时间:"+ timeUtil.getAvrTimeUs() + " Us"); timeUtil1.start();
result = serialize.deserialize(serByte,NettyMessage.class);
timeUtil1.end(); }
System.out.println("序列化时间:"+ timeUtil.getAvrTimeUs() + " Us");
System.out.println("反序列化时间:"+ timeUtil1.getAvrTimeUs() + " Us"); System.out.println("结果:" + result); }
这里定义了一个TimeUtil类来计时
public class TimeUtil {
private long startTime;
private long endTime;
private long timeSum;
private long count;
public void init(){
timeSum = 0;
count = 0;
}
public void start(){
startTime = System.nanoTime();
}
public void end(){
endTime = System.nanoTime();
timeSum += (endTime-startTime);
count++;
}
public long getAvrTimeNs(){
return (timeSum/count);
}
public long getAvrTimeUs(){
return (timeSum/count)/1000;
}
public long getAvrTimeMs(){
return (timeSum/count)/1000000;
}
}
| 码流大小(byte) | 10次(us) | 100次(us) | 1000次(us) | 10000次(us) | 100000次(us) | ||
| FastJson | 305 | 116-243 | 106-185 | 90-140 | 26-39 | 8-12 | |
| JDK | 866 | 383-777 | 502-1101 | 123-334 | 54-237 | 15-76 | |
| Hessian | 520 | 959-3836 | 376-567 | 191-329 | 99-161 | 30-47 | |
| Protostuff | 193 | 103-145 | 90-137 | 75-135 | 15-24 | 5-8 | |
注:
1. 码流单位为字节
2. 序列化耗时-反序列化耗时,单位为微秒
从以上测试可以看出
1. JDK方式的码流最大,不利于网络传输。
2. 从整体来看,Prorostuff的码流最小,序列化性能最好。
一些常用Java序列化框架的比较的更多相关文章
- [java]序列化框架性能对比(kryo、hessian、java、protostuff)
序列化框架性能对比(kryo.hessian.java.protostuff) 简介: 优点 缺点 Kryo 速度快,序列化后体积小 跨语言支持较复杂 Hessian 默认支持跨语言 较慢 Pro ...
- Java序列化框架性能比較
博客: http://colobu.com jvm-serializers提供了一个非常好的比較各种Java序列化的的測试套件. 它罗列了各种序列化框架. 能够自己主动生成測试报告. 我在AWS c3 ...
- java序列化框架(protobuf、thrift、kryo、fst、fastjson、Jackson、gson、hessian)性能对比
我们为什么要序列化 举个栗子:下雨天我们要打伞,但是之后我们要把伞折叠起来,方便我们存放.那么运用到我们java中道理是一样的,我们要将数据分解成字节流,以便存储在文件中或在网络上传输,这叫序列 ...
- -1-3 java集合框架基础 java集合体系结构 Collection 常用java集合框架 如何选择集合 迭代器 泛型 通配符概念 Properties 集合 迭代器
集合又称之为容器存储对象的一种方式 •数组虽然也可以存储对象,但长度是固定的:显然需要可变长度的容器 集合和数组的区别? A:长度区别 ...
- Java集合框架(常用类) JCF
Java集合框架(常用类) JCF 为了实现某一目的或功能而预先设计好一系列封装好的具有继承关系或实现关系类的接口: 集合的由来: 特点:元素类型可以不同,集合长度可变,空间不固定: 管理集合类和接口 ...
- Java程序员最常用的8个Java日志框架
转自:http://www.codeceo.com/article/8-java-log-framework.html 作为一名Java程序员,我们开发了很多Java应用程序,包括桌面应用.WEB应用 ...
- 序列化框架性能对比(kryo、hessian、java、protostuff)
简介: 优点 缺点 Kryo 速度快,序列化后体积小 跨语言支持较复杂 Hessian 默认支持跨语言 较慢 Protostuff 速度快,基于protobuf 需静态编译 Protostuff- ...
- 转:Java程序员最常用的8个Java日志框架
作为一名Java程序员,我们开发了很多Java应用程序,包括桌面应用.WEB应用以及移动应用.然而日志系统是一个成熟Java应用所必不可少的,在开发和调试阶段,日志可以帮助我们更好更快地定位bug:在 ...
- 处理异常、常用类、反射、类加载与垃圾回收、java集合框架
异常处理概述 检查异常:检查异常通常是用户错误或者不能被程序员所预见的问题.(cheched) 运行时异常:运行时异常是一个程序在运行过程中可能发生的.可以被程序员避免的异常类型.(Unchecked ...
随机推荐
- 最简单易懂的SpringCloudSleuth教程
事务mapjvm 大佬对下面的说法是否同意呢 能否比较下zipkin,pinpoint,以及skywalking.该如何选型 回答: 他们都提供了分布式服务跟踪的能力,pinpoint以及skywal ...
- PHP设计模式 -- 注册模式
参考文章:https://segmentfault.com/a/1190000007495855 简介 注册树模式又称注册模式或注册器模式.注册树模式通过将对象实例注册到一棵全局的对象树上,需要的时候 ...
- 一文看懂 Github
GitHub 介绍 GitHub 是为开发者构建的一个开发平台.GitHub 是一个受开发者工作方式启发的开发平台,从开源到商业,能够在上面进行托管和查看代码.管理项目和数百万其他开发人员一起开发软件 ...
- WinForm时间选择控件(DateTimePicker)如何选择(显示)时分秒
C# Windows窗体应用中,用到时间选择控件DateTimePicker,发现不能选择时分秒,难道要自己写一个控件?! 答案是否定的,通过属性修改是可以选择时间的,DateTimePicker完全 ...
- 系列博文-LowPoly风格的图像生成器
https://github.com/Ovilia/Polyvia http://www.zhihu.com/question/29856775/answer/57668656 http://zhan ...
- windows下使用 Secure Shell Client工具操作linux常用命令
如果项目部署在linux系统上,而我们使用的是windows系统,那我们可以使用Secure Shell软件进行操作,那怎么使用它来操作tomcat呢? 1. cd /usr/share/apach ...
- kafka 三个配置文件
kafka的配置分为 broker.producter.consumer三个不同的配置 一 BROKER 的全局配置 最为核心的三个配置 broker.id.log.dir.zookeeper.c ...
- 在本地没有安装Oracle的情况下,使用plsql远程连接数据库
远程连接数据库的方式不止一种.这里主要写在本地没有安装Oracle的情况下,使用plsql可视化工具远程连接数据库的方式. 一.前提 首先你本地得安装有plsql工具.具体的安装步骤就不多说了. 二. ...
- document_index_data.go
package types type DocumentIndexData struct { // 文档全文(必须是UTF-8格式),用于生成待索引的关键词 Content string ...
- 隐马尔可夫模型(HMM)总结
摘要: 1.算法概述 2.算法推导 3.算法特性及优缺点 4.注意事项(算法过程,调参等注意事项) 5.实现和具体例子 6.适用场合 内容: 1.算法概述 隐马尔科夫模型(Hidden Markov ...