一些常用Java序列化框架的比较
程序员文章站
2022-06-25 15:53:56
概念 序列化:将Java对象转化为字节数组 反序列化:将字节数组转化为Java对象 在RPC应用中,进行跨进程远程调用的时候,需要使用特定的序列化技术,需要对进行网络传输的对象进行序列化和反序列化。 影响序列化选择有两个因素 1. 序列化之后码流的大小,如果太大,那么将会影响网络传输的性能。 2. ......
概念
序列化:将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的码流最小,序列化性能最好。