源码先锋

源码先锋

序列化与反序列化——FastJSON、Jackson、Gson性能测试

admin 124 166
背景

起因是公司原先用的是阿里开源的FastJSON,大家用的也比较顺手,但是在出现了两次严重的漏洞后,公司决定放弃FastJSON,使用其他序列化/反序列化工具。考虑大家常用的无非就是FastJSON、Jackson和Gson这三种,因此领导让我调研一下到底是使用Gson还是Jackson。

关于漏洞这里我多说一句,建议大家还真得把这个事情当一个事情。我之前就被漏洞坑了一把,在一台linux服务器上部署了6.5版本的confluence,后来阿里云也发紧急通知了,告知赶紧升级,然而我并没有当一回事,过了没两天我就中招了,这台机器被挖矿了,什么都干不了,只能是初始化系统,好一顿折腾~

测试结果预告

FastJSON、Gson、Jackson序列化性能比较.png

FastJSON、Gson、Jackson反序列化性能比较.png

前置准备工作

为了营造一个相对准确、互不影响的测试环境,我们需要有以下限制(要求):

同一台机器,即我的mac:

MacBookPro(16-inch,2019)处理器2.3GHz八核IntelCorei9内存16GB2667MHzDDR4硬盘1T

JVM相关参数配置:

运行参数-Xms4g-Xmx4g-XX:+UseG1GC

三种JSON引擎版本:

:2.11.1:2.8.6:1.2.72
序列化与反序列化

搞事情之前,我们先来复习一下什么是序列化与反序列化:

序列化:把Java对象转换为字节序列的过程。

反序列化:把字节序列恢复为Java对象的过程。

对象的序列化主要有两种用途:

持久化对象:把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;

网络传输对象:在网络上传送对象的字节序列。

代码测试

创建一个对象,包含Boolean、Integer、Long、Double、Date、String、ArrayList、HashMap等数据类型,构造方法即初始化好对象,方便后面使用:

;;;;;;;;/***数据对象*@authorjava架构设计*@date2020/7/175:19下午**/@DatapublicclassDataBeanimplementsSerializable{privatestaticfinallongserialVersionUID=6453520211539229613L;privateBooleanaBoolean;privateIntegerinteger;privateLongaLong;privateDoubleaDouble;privateDatedate;privateStringstring;privateListStringlist;privateMapString,ObjectobjectMap;publicDataBean(){Randomrandom=newRandom();=();=();=();=();=newDate();=();=();=();}}

其中randomString()、randomList()、randomMap()方法如下:

;*;/***数据构造器**@authorjava架构设计*@date2020/7/175:30下午**/publicclassDataBuilder{privatestaticfinalString[]chars=newString[]{"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};privatestaticfinalRandomrandom=newRandom();/***生成100以内随机长度的字符串*@returnString*/publicstaticStringrandomString(){intlen=(100);StringBuildersb=newStringBuilder(len);for(inti=0;ilen;i++){(chars[(62)]);}();}/***生成100以内的字符串集合*@returnListString*/publicstaticListStringrandomList(){intlen=(100);ArrayListStringlist=newArrayList(len);for(inti=0;ilen;i++){(randomString());}returnlist;}/***生成100以内的Map*@returnMapString,Object*/publicstaticMapString,ObjectrandomMap(){intlen=(100);MapString,Objectmap=newHashMap(len);for(inti=0;ilen;i++){switch(i%6){case0:("key"+i,());break;case1:("key"+i,());break;case2:("key"+i,());break;case3:("key"+i,());break;case4:("key"+i,newDate());break;case5:("key"+i,randomString());break;default:break;}}returnmap;}}

序列化测试代码:

测试对象数分别为1、100、1000、10000、100000个,对象都是预先生成好,然后再依次执行三种JSON引擎执行序列化操作,输出结果:

10个对象FastJSON耗时:87msGson耗时:21msJackson耗时:54ms1000个对象FastJSON耗时:138msGson耗时:114msJackson耗时:98ms10000个对象FastJSON耗时:1657msGson耗时:3340msJackson耗时:1456ms

生成条形图:

FastJSON、Gson、Jackson序列化性能比较.png

从上图来分析,可以知道的是,在数据量较少(1、10、100)的时候,Gson的性能最优,且优势较明显,当对象数量在1000的时候,Jackson的性能开始上来了,因此在对象数量在1~1000的时候,性能比拼:GsonJacksonFastJSON。

但是当数量达到10000、100000级别的时候,Gson的性能下降的比较厉害,而FastJson和Jackson依旧保持着它们的快,性能比较:JacksonFastJSONGson。

反序列化测试代码:

publicstaticvoidmain(String[]args){testDeSerialize(1);//testDeSerialize(10);//testDeSerialize(100);//testDeSerialize(1000);//testDeSerialize(10000);//testDeSerialize(100000);}

测试对象数分别为1、100、1000、10000、100000个,对象都是预先序列化好,然后再依次执行三种JSON引擎执行反序列化操作,输出结果:

10次FastJSON耗时:29msGson耗时:15msJackson耗时:34ms1000次FastJSON耗时:131msGson耗时:214msJackson耗时:146ms100000次FastJSON耗时:5985msGson耗时:7357msJackson耗时:5232ms

生成条形图:

FastJSON、Gson、Jackson反序列化性能比较.png

反序列化性能测试,

在对象数量为1、10、100的时候,Gson的性能最好,Jackson次之,性能排序为:GsonJacksonFastJson;

在对象数量为1000、10000的时候,Gson的性能下降比较明显,这个量级下性能排序为:FastJsonJacksonGson。

在对象数量为10w的时候,Jackson反超FastJson,性能排序为:JacksonFastJsonGson。

总结

当数据量较小的时候(1~100),建议使用Gson;

当数据量较大的时候,建议使用Jackson;

在大数据量的时候,虽然FastJson优势上来了,但是因为有漏洞,不得不放弃。

在做这个性能测试之前,也是在网上查找了一下大家是怎么做这个性能测试的,有的很复杂,有的却也简单,最终我还是选择了一种简单的测试方式,在尽可能的规避其他因素的影响,比如:提前生成好数据、三种引擎测试的样本数据一致、1~10w次的次数测试的时候都是重新运行main方法,尽量避免虚拟机的影响。

最终得出的性能测试结果与网上参考文章的测试结果一致。