Fastjson内幕

来源:互联网 发布:mac美服lol加速器 编辑:程序博客网 时间:2024/04/30 23:14

转载:http://wenshao.iteye.com/blog/1142031/

JSON协议使用方便,越来越流行。JSON的处理器有很多,为什么需要再写一个呢?因为我们需要一个性能很好的JSON Parser,希望JSON Parser的性能有二进制协议一样好,比如和protobuf一样,这可不容易,但确实做到了。有人认为这从原理上就是不可能的,但是计算机乃实践科学,看实际的结果比原理推导更重要。 


这篇文章告诉大家: 
* Fastjson究竟有多快 
* 为什么Fastjson这么快 
* 你能用Fastjson来做什么! 
* 如何获得fastjson? 

首先,Fastjson究竟有多快? 
我们看一下使用https://github.com/eishay/jvm-serializers/提供的程序进行测试得到的结果: 
 序列化时间反序列化时间大小压缩后大小java序列化865443787889541hessian672510460501313protobuf29641745239149thrift31771949349197avro35201948221133json-lib45788149741485263jackson30524161503271fastjson25951472468251
这是一个468bytes的JSON Bytes测试,从测试结果来看,无论序列化和反序列化,Fastjson超越了protobuf,可以当之无愧fast! 它比java deserialize快超过30多倍,比json-lib快100倍。由于Fastjson的存在,你可以放心使用json统一协议,达到文本协议的可维护性,二进制协议的性能。 

JSON处理主要包括两个部分,serialize和deserialize。serialize就是把Java对象变成JSON String或者JSON Bytes。Deserialize是把JSON String或者Json Bytes变成java对象。其实这个过程有些JSON库是分三部分的,json string <--> json tree <--> java object。Fastjson也支持这种转换方式,但是这种转换方式因为有多余的步骤,性能不好,不推荐使用。 

为什么Fastjson能够做到这么快? 
一、Fastjson中Serialzie的优化实现 
1、自行编写类似StringBuilder的工具类SerializeWriter。 
把java对象序列化成json文本,是不可能使用字符串直接拼接的,因为这样性能很差。比字符串拼接更好的办法是使用java.lang.StringBuilder。StringBuilder虽然速度很好了,但还能够进一步提升性能的,fastjson中提供了一个类似StringBuilder的类com.alibaba.fastjson.serializer.SerializeWriter。 

SerializeWriter提供一些针对性的方法减少数组越界检查。例如public void writeIntAndChar(int i, char c) {},这样的方法一次性把两个值写到buf中去,能够减少一次越界检查。目前SerializeWriter还有一些关键的方法能够减少越界检查的,我还没实现。也就是说,如果实现了,能够进一步提升serialize的性能。 

2、使用ThreadLocal来缓存buf。 
这个办法能够减少对象分配和gc,从而提升性能。SerializeWriter中包含了一个char[] buf,每序列化一次,都要做一次分配,使用ThreadLocal优化,能够提升性能。 

3、使用asm避免反射 
获取java bean的属性值,需要调用反射,fastjson引入了asm的来避免反射导致的开销。fastjson内置的asm是基于objectweb asm 3.3.1改造的,只保留必要的部分,fastjson asm部分不到1000行代码,引入了asm的同时不导致大小变大太多。 

使用一个特殊的IdentityHashMap优化性能。 
fastjson对每种类型使用一种serializer,于是就存在class -> JavaBeanSerizlier的映射。fastjson使用IdentityHashMap而不是HashMap,避免equals操作。我们知道HashMap的算法的transfer操作,并发时可能导致死循环,但是ConcurrentHashMap比HashMap系列会慢,因为其使用volatile和lock。fastjson自己实现了一个特别的IdentityHashMap,去掉transfer操作的IdentityHashMap,能够在并发时工作,但是不会导致死循环。 

5、缺省启用sort field输出 
json的object是一种key/value结构,正常的hashmap是无序的,fastjson缺省是排序输出的,这是为deserialize优化做准备。 

6、集成jdk实现的一些优化算法 
在优化fastjson的过程中,参考了jdk内部实现的算法,比如int to char[]算法等等。 

二、fastjson的deserializer的主要优化算法 
deserializer也称为parser或者decoder,fastjson在这方面投入的优化精力最多。 
1、读取token基于预测。 
所有的parser基本上都需要做词法处理,json也不例外。fastjson词法处理的时候,使用了基于预测的优化算法。比如key之后,最大的可能是冒号":",value之后,可能是有两个,逗号","或者右括号"}"。在com.alibaba.fastjson.parser.JSONScanner中提供了这样的方法: 
Java代码  收藏代码
  1. public void nextToken(int expect) {  
  2.     for (;;) {  
  3.         switch (expect) {  
  4.             case JSONToken.COMMA: //   
  5.                 if (ch == ',') {  
  6.                     token = JSONToken.COMMA;  
  7.                     ch = buf[++bp];  
  8.                     return;  
  9.                 }  
  10.   
  11.                 if (ch == '}') {  
  12.                     token = JSONToken.RBRACE;  
  13.                     ch = buf[++bp];  
  14.                     return;  
  15.                 }  
  16.   
  17.                 if (ch == ']') {  
  18.                     token = JSONToken.RBRACKET;  
  19.                     ch = buf[++bp];  
  20.                     return;  
  21.                 }  
  22.   
  23.                 if (ch == EOI) {  
  24.                     token = JSONToken.EOF;  
  25.                     return;  
  26.                 }  
  27.                 break;  
  28.         // ... ...  
  29.     }  
  30. }  

从上面摘抄下来的代码看,基于预测能够做更少的处理就能够读取到token。 

2、sort field fast match算法 
fastjson的serialize是按照key的顺序进行的,于是fastjson做deserializer时候,采用一种优化算法,就是假设key/value的内容是有序的,读取的时候只需要做key的匹配,而不需要把key从输入中读取出来。通过这个优化,使得fastjson在处理json文本的时候,少读取超过50%的token,这个是一个十分关键的优化算法。基于这个算法,使用asm实现,性能提升十分明显,超过300%的性能提升。 
Java代码  收藏代码
  1. "id" : 123"name" : "魏加流""salary" : 56789.79}  
  2.   ------      --------          ----------    

在上面例子看,虚线标注的三个部分是key,如果key_id、key_name、key_salary这三个key是顺序的,就可以做优化处理,这三个key不需要被读取出来,只需要比较就可以了。 

这种算法分两种模式,一种是快速模式,一种是常规模式。快速模式是假定key是顺序的,能快速处理,如果发现不能够快速处理,则退回常规模式。保证性能的同时,不会影响功能。 

在这个例子中,常规模式需要处理13个token,快速模式只需要处理6个token。 

实现sort field fast match算法的代码在这个类[com.alibaba.fastjson.parser.deserializer.ASMDeserializerFactory|http://code.alibabatech.com/svn/fastjson/trunk/fastjson/src/main/java/com/alibaba/fastjson/parser/deserializer/ASMDeserializerFactory.java],是使用asm针对每种类型的VO动态创建一个类实现的。 
这里是有一个用于演示sort field fast match算法的代码: 
http://code.alibabatech.com/svn/fastjson/trunk/fastjson/src/test/java/data/media/ImageDeserializer.java 
Java代码  收藏代码
  1. // 用于快速匹配的每个字段的前缀  
  2. char[] size_   = "\"size\":".toCharArray();  
  3. char[] uri_    = "\"uri\":".toCharArray();  
  4. char[] titile_ = "\"title\":".toCharArray();  
  5. char[] width_  = "\"width\":".toCharArray();  
  6. char[] height_ = "\"height\":".toCharArray();  
  7.   
  8. // 保存parse开始时的lexer状态信息  
  9. int mark = lexer.getBufferPosition();  
  10. char mark_ch = lexer.getCurrent();  
  11. int mark_token = lexer.token();  
  12.   
  13. int height = lexer.scanFieldInt(height_);  
  14. if (lexer.matchStat == JSONScanner.NOT_MATCH) {  
  15.     // 退出快速模式, 进入常规模式  
  16.     lexer.reset(mark, mark_ch, mark_token);  
  17.     return (T) super.deserialze(parser, clazz);  
  18. }  
  19.   
  20. String value = lexer.scanFieldString(size_);  
  21. if (lexer.matchStat == JSONScanner.NOT_MATCH) {  
  22.     // 退出快速模式, 进入常规模式  
  23.     lexer.reset(mark, mark_ch, mark_token);  
  24.     return (T) super.deserialze(parser, clazz);  
  25. }  
  26. Size size = Size.valueOf(value);  
  27.   
  28. // ... ...  
  29.   
  30. // batch set  
  31. Image image = new Image();  
  32. image.setSize(size);  
  33. image.setUri(uri);  
  34. image.setTitle(title);  
  35. image.setWidth(width);  
  36. image.setHeight(height);  
  37.   
  38. return (T) image;  


3、使用asm避免反射 
deserialize的时候,会使用asm来构造对象,并且做batch set,也就是说合并连续调用多个setter方法,而不是分散调用,这个能够提升性能。 

4、对utf-8的json bytes,针对性使用优化的版本来转换编码。 
这个类是com.alibaba.fastjson.util.UTF8Decoder,来源于JDK中的UTF8Decoder,但是它使用ThreadLocal Cache Buffer,避免转换时分配char[]的开销。 
ThreadLocal Cache的实现是这个类com.alibaba.fastjson.util.ThreadLocalCache。第一次1k,如果不够,会增长,最多增长到128k。 
Java代码  收藏代码
  1. //代码摘抄自com.alibaba.fastjson.JSON  
  2. public static final <T> T parseObject(byte[] input, int off, int len, CharsetDecoder charsetDecoder, Type clazz,  
  3.                                       Feature... features) {  
  4.     charsetDecoder.reset();  
  5.   
  6.     int scaleLength = (int) (len * (double) charsetDecoder.maxCharsPerByte());  
  7.     char[] chars = ThreadLocalCache.getChars(scaleLength); // 使用ThreadLocalCache,避免频繁分配内存  
  8.   
  9.     ByteBuffer byteBuf = ByteBuffer.wrap(input, off, len);  
  10.     CharBuffer charByte = CharBuffer.wrap(chars);  
  11.     IOUtils.decode(charsetDecoder, byteBuf, charByte);  
  12.   
  13.     int position = charByte.position();  
  14.   
  15.     return (T) parseObject(chars, position, clazz, features);  
  16. }  


6、symbolTable算法。 
我们看xml或者javac的parser实现,经常会看到有一个这样的东西symbol table,它就是把一些经常使用的关键字缓存起来,在遍历char[]的时候,同时把hash计算好,通过这个hash值在hashtable中来获取缓存好的symbol,避免创建新的字符串对象。这种优化在fastjson里面用在key的读取,以及enum value的读取。这是也是parse性能优化的关键算法之一。 

以下是摘抄自JSONScanner类中的代码,这段代码用于读取类型为enum的value。 
Java代码  收藏代码
  1. int hash = 0;  
  2. for (;;) {  
  3.     ch = buf[index++];  
  4.     if (ch == '\"') {  
  5.         bp = index;  
  6.         this.ch = ch = buf[bp];  
  7.         strVal = symbolTable.addSymbol(buf, start, index - start - 1, hash); // 通过symbolTable来获得缓存好的symbol,包括fieldName、enumValue  
  8.         break;  
  9.     }  
  10.       
  11.     hash = 31 * hash + ch; // 在token scan的过程中计算好hash  
  12.   
  13.     // ... ...  
  14. }  


我们能用fastjson来作什么? 
1、替换其他所有的json库,java世界里没有其他的json库能够和fastjson可相比了。 
2、使用fastjson的序列化和反序列化替换java serialize,java serialize不单性能慢,而且体制大。 
3、使用fastjson替换hessian,json协议和hessian协议大小差不多一样,而且fastjson性能优越,10倍于hessian 
4、把fastjson用于memached缓存对象数据。 


如何获得fastjson 
h3. 官方网站 
Fastjson是开源的,基于Apache 2.0协议。你可以在官方网站了解最新信息。 
http://code.alibabatech.com/wiki/display/FastJSON/Home 

maven用户 
* Maven仓库 http://code.alibabatech.com/mvn/releases/ 
{code} 
<dependency> 
     <groupId>com.alibaba</groupId> 
     <artifactId>fastjson</artifactId> 
     <version>1.1.2</version> 
</dependency> 
{code} 

Downlaods 
Binary : http://code.alibabatech.com/mvn/releases/com/alibaba/fastjson/1.1.2/fastjson-1.1.2.jar 
Source :http://code.alibabatech.com/mvn/releases/com/alibaba/fastjson/1.1.2/fastjson-1.1.2-sources.jar 
Subversion : http://code.alibabatech.com/svn/fastjson/trunk/fastjson/
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 华为v9手机激活密码忘了怎么办 公司报销发票纸质的丢了怎么办 在京东上买的小天才手表坏了怎么办 美亚买东西过几天降价了怎么办 天猫买东西退款后又不想退了怎么办 自己写错了地址快递被签收了怎么办 在天猫购买东西不发货怎么办 在天猫买了东西不给我发货怎么办 天猫退货卖家收到货不退款怎么办 唯品会的账号找不回来了怎么办 从网上买的沙发物流超级慢怎么办 三星c5手机玩王者荣耀卡怎么办 买手机送话费的卡不想用了怎么办 到银行存钱被骗买保险了怎么办 去银行存钱被骗买了保险怎么办 京东申请退款后卖家又发货了怎么办 苹果6s没有4g网怎么办 花呗分期付款买手机额度不够怎么办 手机天猫不支持购买淘宝商品怎么办 天猫国际购买商品狠盾怎么办 在微信上微拍堂买东西被骗了怎么办 京东自营买到返修手机怎么办? 7p弯了怎么办能修复吗 农商银行app登录密码忘了怎么办 网址上的重庆时时彩不能提现怎么办 天天中彩票自己账户登录不了怎么办 天猫上卖王者荣耀的兑换码是怎么办 身份证借别人开淘宝永久封店怎么办 天猫店铺被扣12分怎么办 天猫法人变更之前的天猫贷款怎么办 在日本旅游遇到精日导游怎么办 银行资金交易异常卡被冻结怎么办 如果淘宝被盗了店铺乱上东西怎么办 快递不送货直接代售点签收怎么办 淘宝快递没收到却显示已签收怎么办 支付宝余额未满16受限怎么办 未满16岁支付宝余额受限怎么办 手机天猫购物买的数量大怎么办 扣扣游戏领礼包出现账号异常怎么办 美容院转让给别人客人要退钱怎么办 卖家毁约中介费担保费怎么办