fastjson有一个问题,我不知道是bug,还是我不会用,请求高人指点 工作

fastjson反序列问题 - 开源中国社区
当前访客身份:游客 [
当前位置:
最近在做个类似Ext.Direct 的jquery 插件,这个插件写好了,服务器段用java实现的时候遇到了一个fastjson&反系列化的问题,需要把需要请求的json 反序列化&DirectRequest &
主要几个字段 action(把请求映射的某个类) method(把请求映射的某个action类的某个方法) data (传递给方法的参数)如&{"action":"Echo","method":"getByDate","data":[" 11:23:34"]}
如果方法有参数 请求json data 不是空 可以正常反序列化,但是 如果方法没有参&数,json data 为null &如&{"action":"Echo","method":"getByDate","data":null} 这样反序列时就会出错com.alibaba.fastjson.JSONException: syntax error, expect [, actual null, pos 6
是我使用fastjson 不对,还是fastjson 有bug
---------------问题补充---------------
:DirectRequest类
import com.alibaba.fastjson.JSONA
public class DirectRequest {
private JSONA
public String getAction() {
public void setAction(String action) {
this.action =
public String getMethod() {
public void setMethod(String method) {
this.method =
public JSONArray getData() {
public void setData(JSONArray data) {
this.data =
import com.alibaba.fastjson.JSON;
import entity.DirectR
public class DirectRequestTest {
public static void main(String[] args) {
String str = "{\"action\":\"Echo\",\"method\":\"getByDate\",\"data\":null}";
DirectRequest req= JSON.parseObject(str,DirectRequest.class);
共有4个回答
<span class="a_vote_num" id="a_vote_num_
DirectRequest类的第三个属性是JSONArray类型,所以异常提示“expect [”,改成:
String str = "{\"action\":\"Echo\",\"method\":\"getByDate\",\"data\":[]}";
<span class="a_vote_num" id="a_vote_num_
String str = "{\"action\":\"Echo\",\"method\":\"getByDate\",\"data\":null}";
TmpModel model = JSON.parseObject(str,TmpModel.class);
测试未发现楼主这样的问题,把反序列化的代码贴出来看看
<span class="a_vote_num" id="a_vote_num_
DirectRequest类
import com.alibaba.fastjson.JSONA
public class DirectRequest { private S private S& private JSONA public String getAction() { } public void setAction(String action) { this.action = } public String getMethod() { } public void setMethod(String method) { this.method = } public JSONArray getData() { } public void setData(JSONArray data) { this.data = } }
import com.alibaba.fastjson.JSON;
import entity.DirectR
public class DirectRequestTest { & &public static void main(String[] args) { & String str = "{\"action\":\"Echo\",\"method\":\"getByDate\",\"data\":null}";& & DirectRequest req= JSON.parseObject(str,DirectRequest.class); && } }
--- 共有 1 条评论 ---
把data设置成List类型
(2年前)&nbsp&
<span class="a_vote_num" id="a_vote_num_
引用来自“优雅先生”的评论
DirectRequest类的第三个属性是JSONArray类型,所以异常提示“expect [”,改成:
String str = "{\"action\":\"Echo\",\"method\":\"getByDate\",\"data\":[]}";
你说的我知道,还可以json不要data 属性 String str = "{\"action\":\"Echo\",\"method\":\"getByDate\"}"; &只不过jquery插件我是仿写Ext.Direct,它是没有参数 data:null 我觉得这样好理解些,写成 data:[] 让人觉得方法还是需要参数一样,我是希望&String str = "{\"action\":\"Echo\",\"method\":\"getByDate\",\"data\":null}";反序列化后DirectRequest 的data 属性为null
更多开发者职位上
有什么技术问题吗?
jacksno...的其它问题
类似的话题本篇接下来的内容是基于FastJSON 1.1.40,着重讲述其序列化、反序列化实现,最后分析FastJSON为何如此“fast”的原因。
所谓序列化,就是将java各种对象转化为json串。不多说,先上图。
&序列化入口
平常我们经常用到的是JSON.toJSONString()这个静态方法来实现序列化。其实JSON是一个抽象类,该类实现了JSONAware(转为json串)和JSONStreamAware(将json串写入Appendable中)的接口,同时又是JSONArray(内部实现就是个List)和JSONObject(内部实现就是个Map)的父类。JSON.toJSONString()方法内部实现基本相同,为做某些特定配置,对外暴露的接口可能不同。该方法的实现实际托付给了JSONSerializer类。
序列化组合器
JSONSerializer类相当于一个序列化组合器,它将上层调用、序列化配置、具体类型序列化实现、序列化字符串拼接等功能组合在一起,方便外部统一调用。该类有几个重要的成员,SerializeConfig、SerializeWriter、各种Filter列表、DateFormat、SerialContext等,还有每次对各个具体对象序列化的ObjectSerializer(非JSONSerializer的成员变量)。下面就来挨个说明其各自功能。
1.& SerializeConfig
SerializeConfig是全局唯一的,它继承自IdentityHashMap,IdentityHashMap是一个长度默认为1024的Hash桶,每个桶存放相同Hash的Entry(可看做链表节点,包含key、value、next指针、hash值)做成的单向链表,IdentityHashMap实现了HashMap的功能,但能避免HashMap并发时的死循环。 &
SerializeConfig的主要功能是配置并记录每种Java类型对应的序列化类(ObjectSerializer接口的实现类),比如Boolean.class使用BooleanCodec(看命名就知道该类将序列化和反序列化实现写到一起了)作为序列化实现类,float[].class使用FloatArraySerializer作为序列化实现类。这些序列化实现类,有的是FastJSON中默认实现的(比如Java基本类),有的是通过ASM框架生成的(比如用户自定义类),有的甚至是用户自定义的序列化类(比如Date类型框架默认实现是转为毫秒,应用需要转为秒)。当然,这就涉及到是使用ASM生成序列化类还是使用JavaBean的序列化类类序列化的问题,这里判断根据就是是否Android环境(环境变量"java.vm.name"为"dalvik"或"lemur"就是Android环境),但判断不仅这里一处,后续还有更具体的判断。
2.&&SerializeWriter
SerializeWriter继承自Java的Writer,其实就是个转为FastJSON而生的StringBuilder,完成高性能的字符串拼接。该类成员如下:
char buf[]
&可理解为每次序列化后字符串的内存存放地址。
static ThreadLocal& bufLocal&
每次序列化,都需要重新分配buf[]内存空间。而bufLocal就是每次序列化后bug[]的内存空间保留到ThreadLocal里,但其中的值清空,避免频繁的内存分配和gc。
int features&
生成json字符串的特征配置,默认配置为:&
&span&QuoteFieldNames | SkipTransientField | WriteEnumUsingToString | SortField&/span&
&表示含义为:双引号filedName and 忽略transientField and enum类型使用String写入 and 排序输出field。&支持的所有特征在SerializerFeature类中,用户可在调用时显示配置,也可通过JSONFiled或JSONType注入配置。
writer&用户指定将生成的json串直接写入某writer中,比如JSONWriter类。
举个例子吧,writeStringWithDoubleQuote()表示用字符串用双引号写入,看看如何拼接字符串的。
3.&&Filter列表
SerializeWriter中有很多Filter列表,可视为在生成json串的各阶段、各地方定制序列化,大致如下:
BeforeFilter :序列化时在最前面添加内容
AfterFilter :序列化时在最后面添加内容
PropertyFilter :根据PropertyName和PropertyValue来判断是否序列化
ValueFilter :修改Value
NameFilter :修改key
PropertyPreFilter :根据PropertyName判断是否序列化
4.& DateFormat
指定日期格式。若不指定,FastJSON会自动识别如下日期格式:
ISO-8601日期格式
yyyy-MM-dd
yyyy-MM-dd HH:mm:ss
yyyy-MM-dd HH:mm:ss.SSS
毫秒字符串
.Net Json日期格式
new Date()
5.&&SerialContext
序列化上下文,在引用或循环引用中使用,该值会放入references的Hash桶(IdentityHashMap)缓存。
6.&&ObjectSerializer&&
ObjectSerializer只有一个接口方法,如下:
void write(JSONSerializer serializer,Objectobject,Object
fieldName,Type fieldType);
可见,将JSONSerializer传入了ObjectSerializer中,而JSONSerializer有SerializeWriter成员,在每个具体ObjectSerializer实现中,直接使用SerializeWriter拼接字符串即可;Object即是待序列化的对象;fieldName则主要用于组合类引用时设置序列化上下文;而fieldType主要是为了泛型处理。 &
JSONSerializer中通过public ObjectSerializer getObjectWriter(Class clazz)函数获取类对应的序列化类(即实现ObjectSerializer接口的类),大致逻辑如下:
整个过程是先获取已实现基础类对应的序列化类,再通过类加载器获取自定义的AutowiredObjectSerializer序列化类,最后获取通过createJavaBeanSerializer()创建的序列化类。通过该方法会获取两种序列化类,一种是直接的JavaBeanSerializer(根据类的get方法、public filed等JavaBean特征序列化),另一种是createASMSerializer(通过ASM框架生成的序列化字节码),优先使用第二种。选择JavaBeanSerializer的条件为:
该clazz为非public类
该clazz的类加载器在ASMClassLoader的外部,或者clazz就是 Serializable.class,或者clazz就是Object.class
JSONType的注解指明不适用ASM
createASMSerializer加载失败&
结合前面的讨论,可以得出使用ASM的条件:非Android系统、非基础类、非自定义的AutowiredObjectSerializer、非以上所列的使用JavaBeanSerializer条件。&
具体基础类的序列化方法、JavaBeanSerializer的序列化方法和ASM生成的序列化方法可以参见代码,这里就不做一一讲解了。
2. 反序列化
所谓反序列化,就是将json串转化为对应的java对象。还是先上图。
同样是JSON类作为反序列化入口,实现了parse()、parseObject()、parseArray()等将json串转换为java对象的静态方法。这些方法的实现,实际托付给了DefaultJSONParser类。 &&
DefaultJSONParser类相当于序列化的JSONSerializer类,是个功能组合器,它将上层调用、反序列化配置、反序列化实现、词法解析等功能组合在一起,相当于设计模式中的外观模式,供外部统一调用。同样,我们来分析该类的几个重要成员,看看他是如何实现纷繁的反序列化功能的。
1.&&ParserConfig
同SerializeConfig,该类也是全局唯一的解析配置,其中的boolean asmEnable同样判断是否为Andriod环境。与SerializeConfig不同的是,配置类和对应反序列类的IdentityHashMap是该类的私有成员,构造函数的时候就将基础反序列化类加载进入IdentityHashMap中。
2.&&JSONLexer&
JSONLexer是个接口类,定义了各种当前状态和操作接口。JSONLexerBase是对JSONLexer实现的抽象类,类似于序列化的SerializeWriter类,专门解析json字符串,并做了很多优化。实际使用的是JSONLexerBase的两个子类JSONScanner和JSONLexerBase,前者是对整个字符串的反序列化,后者是接Reader直接序列化。简析JSONLexerBase的某些成员:
由于json串具有一定格式,字符串会根据某些特定的字符来自解释所表示的意义,那么这些特定的字符或所处位置的字符在FastJSON中就叫一个token,比如"(","{","[",",",":",key,value等,这些都定义在JSONToken类中。
char[] sbuf
解析器通过扫描输入字符串,将匹配得到的最细粒度的key、value会放到sbuf中。
static ThreadLocal& SBUF_REF_LOCAL
上面sbuf的空间不释放,在下次需要的时候直接拿出来使用,从避免的内存的频繁分配和gc。
反序列化特性的配置,同序列化的feature是通过int的位或来实现其特性开启还是关闭的。默认配置是: AutoCloseSource | UseBigDecimal | AllowUnQuotedFieldNames | AllowSingleQuotes | AllowArbitraryCommas | AllowArbitraryCommas | SortFeidFastMatch | IgnoreNotMatch ,表示检查json串的完整性 and 转换数值使用BigDecimal and 允许接受不使用引号的filedName and 允许接受使用单引号的key和value and 允许接受连续多个","的json串 and 使用排序后的field做快速匹配 and 忽略不匹配的key/value对。当然,这些参数也是可以通过其他途径配置的。
hasSpecial
对转义符的处理,比如'\0','\'等。
词法解析器是基于预测的算法从左到右一次遍历的。由于json串具有自身的特点,比如为key的token后最有可能是":",":"之后可能是value的token或为"{"的token或为"["的token等等,从而可以根据前一个token预判下一个token的可能,进而得知每个token的含义。分辨出各个token后,就可以获取具体值了,比如scanString获取key值,scanFieldString根据fieldName获取fieldValue,scanTrue获取java的true等等。其中,一般会对key进行缓存,放入SymbolTable(类似于IdentityHashMap)中,猜想这样做的目的是:应用解析的json串一般key就那么多,每次生成开销太多,干脆缓存着,用的就是就来取,还是空间换时间的技巧。
3.&&List& ExtraTypeProvider &和List& ExtraProcessor &
视为对其他类型的处理和其他自定义处理而留的口子,用户可以自己实现对应接口即可。
4.&&DateFormat
同序列化的DateFormat,不多说了。
5.&&ParseContext 和 List& ResolveTask &
ParseContext同序列化的SerialContext,为引用甚至循环引用做准备。 &&
List& ResolveTask &当然就是处理这种多层次甚至多重引用记录的list了。
6.&&SymbolTable
上面提到的key缓存。
7.&&ObjectDeserializer
跟ObjectSerializer也是相似的。先根据fieldType获取已缓存的解析器,如果没有则根据fieldClass获取已缓存的解析器,否则根据注解的JSONType获取解析器,否则通过当前线程加载器加载的AutowiredObjectDeserializer查找解析器,否则判断是否为几种常用泛型(比如Collection、Map等),最后通过createJavaBeanDeserializer来创建对应的解析器。当然,这里又分为JavaBeanDeserializer和asmFactory.createJavaBeanDeserializer两种。使用asm的条件如下:
非Android系统
该类及其除Object之外的所有父类为是public的
泛型参数非空
非asmFactory加载器之外的加载器加载的类
类的setter函数不大于200
类有默认构造函数
类不能含有仅有getter的filed
类不能含有非public的field
类不能含有非静态的成员类
类本身不是非静态的成员类
使用ASM生成的反序列化器具有较高的反序列化性能,比如对排序的json串可按顺序匹配解析,从而减少读取的token数,但如上要求也是蛮严格的。综上,FastJSON反序列化也支持基础反序列化器、JavaBeanDeserializer反序列化器和ASM构造的反序列化器,这里也不做一一讲解了。
3. Why So Fast
FastJSON真的很快,读后受益匪浅。个人总结了下快的原因(不一定完整):
1.&&专业的心做专业的事
不论是序列化还是反序列化,FastJSON针对每种类型都有与之对应的序列化和反序列化方法,就针对这种类型来做,优化性能自然更具针对性。自编符合json的SerializeWriter和JSONLexer,就连ASM框架也给简化掉了,只保留所需部分。不得不叹其用心良苦。
2.&&无处不在的缓存
空间换时间的想法为程序员屡试不爽,而作者将该方法用到任何细微之处:类对应的序列化器/反序列化器全部存起来,方便取用;解析的key存起来,表面重复内存分配等等。
3.&&不厌其烦的重复代码
我不知道是否作者故意为之,程序中出现了很多类似的代码,比如特殊字符处理、不同函数对相同token的处理等。这样虽对于程序员寻求规整相违背,不过二进制代码却很喜欢,无形之中减少了许多函数调用。
4.&&不走寻常路
对于JavaBean,可以通过发射实现序列化和反序列化(FastJSON已有实现),但默认使用的是ASM框架生成对应字节码。为了性能,无所不用其极。
5.&&一点点改变有很大的差别
排序对输出仅是一点小小的改变,丝毫不影响json的使用,但却被作者用在了解析的快速匹配上,而不用挨个拎出key。
6.&&从规律中找性能
上面也讲到,FastJSON读取token基于预测的。json串自身的规律性被作者逮个正着,预测下一个将出现的token处理比迷迷糊糊拿到一个token再分情况处理更快捷。
不喜欢虎头蛇尾的结局。不过写到这里,除了承认自己对FastJSON代码某些地方还没看懂或理解有偏颇之外,不敢说太多了。
阅读(...) 评论()你的位置: >
> FastJSON序列化特殊字符BUG
所有小于等于1.1.39的版本均有此问题
当序列化的任何一个字符串或者可能成为字符串的变量中包含UNICODE码的一些特殊字符时将会抛出异常导致序列化失败。
抛出以下异常:
12345678910
java.lang.ArrayIndexOutOfBoundsException: 128&&&&at com.alibaba.fastjson.serializer.SerializeWriter.writeStringWithDoubleQuote(SerializeWriter.java:868)&&&&at com.alibaba.fastjson.serializer.SerializeWriter.writeFieldName(SerializeWriter.java:1517)&&&&at com.alibaba.fastjson.serializer.MapSerializer.write(MapSerializer.java:156)&&&&at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:369)&&&&at com.alibaba.fastjson.JSON.toJSONString(JSON.java:418)&&&&at com.alibaba.fastjson.JSON.toJSONString(JSON.java:406)&&&&at com.alibaba.fastjson.JSON.toJSONString(JSON.java:565)&&&&at Main.main(Main.java:22)&
出现的条件
待序列化的数据中包含\u0080到\u00A0的特殊字符
且包含2个以上包括2个除此之外的特殊字符,比如\u0001
调用JSON.toString()
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
import com.alibaba.fastjson.JSON;&/** * @author L && * @version 1.0.0 & 21:54& * @since JDK1.6 */public class Main {&&&&public static void main(String[] args) {&&&&&&&&&for (char x = 'u0080'; x &= 'u0080'; ++x) {&&&&&&&&&&&&A a = new A();&&&&&&&&&&&&a.setKey("u0001" + x + "u0002");&&&&&&&&&&&&a.setValue(0);&&&&&&&&&&&&&try {&&&&&&&&&&&&&&&&JSON.toJSONString(a, false);&&&&&&&&&&&&} catch (Exception e) {&&&&&&&&&&&&&&&&System.out.println((int)x + " : " + e.getMessage());&&&&&&&&&&&&&&&&e.printStackTrace();&&&&&&&&&&&&}&&&&&&&&}&&&&}}&&class A {&&&&private String key;&&&&private Object value;&&&&&public String getKey() {&&&&&&&&return key;&&&&}&&&&&public void setKey(String key) {&&&&&&&&this.key = key;&&&&}&&&&&public Object getValue() {&&&&&&&&return value;&&&&}&&&&&public void setValue(Object value) {&&&&&&&&this.value = value;&&&&}}&&
以上代码是重现大多数使用场景,其实对简单的可以通过一行语句直接重现错误JSON.toJSONString("\u\u0002");
这里对其中一种数据类型(MAP)的现场进行分析,另外几种类型基本类似可以举一反三(有兴趣的同学可以试一试)。
首先需要抱怨一下FastJSON糟糕的代码质量,全篇几乎无注释非常不友好,GITHUB上单元测试也是凌乱不堪,真是怀疑公司内部有这么多人追捧为的是什么?难道都没看过源码?
进入正题,其实这个BUG的真正原因也就是低下的代码质量,缺乏管理的测试导致的。首先,我们可以通过异常马上定位到异常的代码段
123456789101112131415161718
package com.alibaba.fastjson.serializer;&public final class SerializeWriter extends Writer {...private void writeStringWithDoubleQuote(String text, final char seperator, boolean checkSpecial) {&&&&...&&&&&&&&&&&&&&&&if (ch & CharTypes.specicalFlags_doubleQuotes.length //&&&&&&&&&&&&&&&&&&&&&& CharTypes.specicalFlags_doubleQuotes[ch] != 0 //&&&&&&&&&&&&&&&&&&&&|| (ch == '/' && isEnabled(SerializerFeature.WriteSlashAsSpecial))) {&&&&&&&&&&&&&&&&&&&&buf[bufIndex++] = '';&&&&&&&&&&&&&&&&&&&&buf[bufIndex++] = replaceChars[(int) ch];&&&&&&&&&&&&&&&&&&&&end++;&&&&&&&&&&&&&&&&} else {&&&&&&&&&&&&&&&&&&&&buf[bufIndex++] = ch;&&&&&&&&&&&&&&&&}...}&
出问题的就是buf[bufIndex++] = replaceChars[(int) ch];这行代码(不得不提一下,这个函数有200多行代码,尼玛)。 进入调试模式,通过观察整个过程的函数栈我们很快就可以掌握整个序列化的处理流程:
如上图所示,整个序列化大致可以是:
JSON.toString(),序列化调用的入口
扫描配置的Featrue选项通过serializer.config设置序列化的方式(比如是否是包含类信息、是否替换特殊字符、是否兼容浏览器显示等等)
serializer.write(object) 开始真正的序列化工作,根据数据类型的不同分别会委派给相应的ObjectSerializer处理(比如Map对应MapSerializer,这里应该学习了Hessian的封装方式,有兴趣的朋友可以比对一下代码)
遍历待序列化对象的所有Field(如果是简单直接可序列类型,比如String、Integer等那么直接一次写结果就OK相当于一次writeValue),调用对应FieldSerializer的writeValue根据数据类型(也是委派)翻译写入JSON的格式
通过write方法写入到JSON Stirng的BUFFER,直接引用JSONSerializer中的SerializeWriter(写入动作还有很多的格式化操作)
返回最终序列化结果
问题就出现在第5步中对写入值的格式化过程中有一项特殊字符的转换操作
如上图所示,FastJSON是通过查表com.alibaba.fastjson.parser.CharTypes.replaceChars的方式来替换不可见的特殊字符为显示UNICODE(即那些\u0001等不会显示为空),但是忽略了80~A0段的空字符处理(\u0080转换为ASCII为128,replaceChars的长度为128,边界溢出)。
上图即位两段特殊字符的对照表,超过02228的特殊字符代码中作了强行过滤所以比较幸运的没有踩到坑。
知道了故障发生的原因,修复问题就非常简单。在最新版的1.1.41版本已经修正次问题,建议大家更新
PATCH LINK:
虽然是一个非常隐晦的BUG受影响面不大,但是由于FASTJSON在淘宝内部使用非常广泛,而FastJson接口中没有显示声明任何要求捕捉的异常导致一般系统在使用时不会对其进行try-catch,那么想象一下如果恶意构造数据导致抛出异常那么可能就会导致数据丢失甚至时系统故障。本站另外一片文章中介绍的另外一个中间件METAQ受这个BUG的影响在特殊状态下会导致消息阻塞无法发送(系统的表现时拒绝服务,LOAD为0)
当然从这里也可以看到字符处理工具往往对特殊字符集的处理容易出现疏漏,所以应该在这类工具中对整个相应的字符集(这里指UNICODE)提供完整的单元测试和回归测试,FASTJSON在这方面做的都时不足的。同时,单元测试代码晦涩、代码几乎没有注释也为后面的BUG埋下了伏笔。
UTF-8字符表:
FastJSON开源库:
转载请注明: & GuestGuestGuest若远
与本文相关的文章> Fastjson黑幕
Fastjson黑幕
xing2323 & &
发布时间: & &
浏览:56 & &
回复:0 & &
悬赏:0.0希赛币
Fastjson内幕
  &转自+Fastjson&
  JSON协议使用方便,越来越流行。JSON的处理器有很多,为什么需要再写一个呢?因为我们需要一个性能很好的JSON Parser,希望JSON Parser的性能有二进制协议一样好,比如和protobuf一样,这可不容易,但确实做到了。有人认为这从原理上就是不可能的,但是计算机乃实践科学,看实际的结果比原理推导更重要。
  这篇文章告诉大家:
Fastjson究竟有多快
为什么Fastjson这么快
你能用Fastjson来做什么!
如何获得fastjson?
首先,Fastjson究竟有多快?
  我们看一下使用提供的程序进行测试得到的结果:
序列化时间
反序列化时间
压缩后大小
java序列化
  测试数据:
  这是一个468bytes的JSON Bytes测试,从测试结果来看,无论序列化和反序列化,Fastjson超越了protobuf,可以当之无愧fast! 它比java deserialize快超过30多倍,比json-lib快100倍。由于Fastjson的存在,你可以放心使用json统一协议,达到文本协议的可维护性,二进制协议的性能。
为什么Fastjson能够做到这么快?
  JSON处理主要包括两个部分,serialize和deserialize。serialize就是把Java对象变成JSON String或者JSON Bytes。Deserialize是把JSON String或者Json Bytes变成java对象。其实这个过程有些JSON库是分三部分的,json string &-& json tree &-& java object。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的同时不导致大小变大太多。
4、使用一个特殊的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中提供了这样的方法:
  public void nextToken(int expect) {
  for (;;) {
  switch (expect) {
  MA: //
  if (ch == ',') {
  token = MA;
  ch = buf[++bp];
  if (ch == '}') {
  token = JSONToken.RBRACE;
  ch = buf[++bp];
  if (ch == ']') {
  token = JSONToken.RBRACKET;
  ch = buf[++bp];
  if (ch == EOI) {
  token = JSONToken.EOF;
  // ... ...
  从上面摘抄下来的代码看,基于预测能够做更少的处理就能够读取到token。
2、sort field fast match算法
  fastjson的serialize是按照key的顺序进行的,于是fastjson做deserializer时候,采用一种优化算法,就是假设key/value的内容是有序的,读取的时候只需要做key的匹配,而不需要把key从输入中读取出来。通过这个优化,使得fastjson在处理json文本的时候,少读取超过50%的token,这个是一个十分关键的优化算法。基于这个算法,使用asm实现,性能提升十分明显,超过300%的性能提升。
  { "id" : 123, "name" : "魏加流", "salary" : 56789.79}
  ------  
--------    
----------
  在上面例子看,虚线标注的三个部分是key,如果key_id、key_name、key_salary这三个key是顺序的,就可以做优化处理,这三个key不需要被读取出来,只需要比较就可以了。
  这种算法分两种模式,一种是快速模式,一种是常规模式。快速模式是假定key是顺序的,能快速处理,如果发现不能够快速处理,则退回常规模式。保证性能的同时,不会影响功能。
  在这个例子中,常规模式需要处理13个token,快速模式只需要处理6个token。
  实现sort field fast match算法的代码在这个类com.alibaba.fastjson.parser.deserializer.ASMDeserializerFactory,是使用asm针对每种类型的VO动态创建一个类实现的。这里是有一个用于演示sort field fast match算法的代码: izer.java
  // 用于快速匹配的每个字段的前缀
  char[] size_  = "\"size\":".toCharArray();
  char[] uri_ 
= "\"uri\":".toCharArray();
  char[] titile_ = "\"title\":".toCharArray();
  char[] width_
= "\"width\":".toCharArray();
  char[] height_ = "\"height\":".toCharArray();
  // 保存parse开始时的lexer状态信息
  int mark = lexer.getBufferPosition();
  char mark_ch = lexer.getCurrent();
  int mark_token = lexer.token();
  int height = lexer.scanFieldInt(height_);
  if (lexer.matchStat == JSONScanner.NOT_MATCH) {
  // 退出快速模式, 进入常规模式
  lexer.reset(mark, mark_ch, mark_token);
  return (T) super.deserialze(parser, clazz);
  String value = lexer.scanFieldString(size_);
  if (lexer.matchStat == JSONScanner.NOT_MATCH) {
  // 退出快速模式, 进入常规模式
  lexer.reset(mark, mark_ch, mark_token);
  return (T) super.deserialze(parser, clazz);
  Size size = Size.valueOf(value);
  // ... ...
  // batch set
  Image image = new Image();
  image.setSize(size);
  image.setUri(uri);
  image.setTitle(title);
  image.setWidth(width);
  image.setHeight(height);
  return (T)
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。
  //代码摘抄自com.alibaba.fastjson.JSON
  public static final &T& T parseObject(byte[] input, int off, int len, CharsetDecoder charsetDecoder, Type clazz,
  Feature... features) {
  charsetDecoder.reset();
  int scaleLength = (int) (len * (double) charsetDecoder.maxCharsPerByte());
  char[] chars = ThreadLocalCache.getChars(scaleLength); // 使用ThreadLocalCache,避免频繁分配内存
  ByteBuffer byteBuf = ByteBuffer.wrap(input, off, len);
  CharBuffer charByte = CharBuffer.wrap(chars);
  IOUtils.decode(charsetDecoder, byteBuf, charByte);
  int position = charByte.position();
  return (T) parseObject(chars, position, clazz, features);
5、symbolTable算法。
  我们看xml或者javac的parser实现,经常会看到有一个这样的东西symbol table,它就是把一些经常使用的关键字缓存起来,在遍历char[]的时候,同时把hash计算好,通过这个hash值在hashtable中来获取缓存好的symbol,避免创建新的字符串对象。这种优化在fastjson里面用在key的读取,以及enum value的读取。这是也是parse性能优化的关键算法之一。
  以下是摘抄自JSONScanner类中的代码,这段代码用于读取类型为enum的value。
  int hash = 0;
  for (;;) {
  ch = buf[index++];
  if (ch == '\"') {
  this.ch = ch = buf[bp];
  strVal = symbolTable.addSymbol(buf, start, index - start - 1, hash); // 通过symbolTable来获得缓存好的symbol,包括fieldName、enumValue
  hash = 31 * hash + // 在token scan的过程中计算好hash
  // ... ...
我们能用fastjson来作什么?
  1、替换其他所有的json库,java世界里没有其他的json库能够和fastjson可相比了。2、使用fastjson的序列化和反序列化替换java serialize,java serialize不单性能慢,而且体制大。3、使用fastjson替换hessian,json协议和hessian协议大小差不多一样,而且fastjson性能优越,10倍于hessian4、把fastjson用于memached缓存对象数据。
如何获得fastjson
  Fastjson是开源的,基于Apache 2.0协议。你可以在官方网站了解最新信息。
Maven仓库   
  &dependency&
  &groupId&com.alibaba&/groupId&
  &artifactId&fastjson&/artifactId&
  &version&1.1.2&/version&
  &/dependency&
  Binary : Source :Subversion :
&rdf:RDF xmlns:rdf=""
xmlns:dc=""
xmlns:trackback=""&
&rdf:Description
rdf:about="+Fastjson"
dc:identifier="+Fastjson"
dc:title="Inside Fastjson"
trackback:ping="" /&
&/rdf:RDF&
Labels parameters
  Labels:
  Enter labels to add to this page:
  Looking for a label
Just start typing.
  10 Comments
comments.show.hide
&rdf:RDF xmlns:rdf=""
xmlns:dc=""
xmlns:trackback=""&
&rdf:Description
rdf:about="+Fastjson focusedCommentId=6947114#comment-6947114"
dc:identifier="+Fastjson focusedCommentId=6947114#comment-6947114"
dc:title="Inside Fastjson"
trackback:ping="" /&
&/rdf:RDF&
&rdf:RDF xmlns:rdf=""
xmlns:dc=""
xmlns:trackback=""&
&rdf:Description
rdf:about="+Fastjson focusedCommentId=6947117#comment-6947117"
dc:identifier="+Fastjson focusedCommentId=6947117#comment-6947117"
dc:title="Inside Fastjson"
trackback:ping="" /&
&/rdf:RDF&
&rdf:RDF xmlns:rdf=""
xmlns:dc=""
xmlns:trackback=""&
&rdf:Description
rdf:about="+Fastjson focusedCommentId=6947133#comment-6947133"
dc:identifier="+Fastjson focusedCommentId=6947133#comment-6947133"
dc:title="Inside Fastjson"
trackback:ping="" /&
&/rdf:RDF&
&rdf:RDF xmlns:rdf=""
xmlns:dc=""
xmlns:trackback=""&
&rdf:Description
rdf:about="+Fastjson focusedCommentId=6947139#comment-6947139"
dc:identifier="+Fastjson focusedCommentId=6947139#comment-6947139"
dc:title="Inside Fastjson"
trackback:ping="" /&
&/rdf:RDF&
&rdf:RDF xmlns:rdf=""
xmlns:dc=""
xmlns:trackback=""&
&rdf:Description
rdf:about="+Fastjson focusedCommentId=6948149#comment-6948149"
dc:identifier="+Fastjson focusedCommentId=6948149#comment-6948149"
dc:title="Inside Fastjson"
trackback:ping="" /&
&/rdf:RDF&
&rdf:RDF xmlns:rdf=""
xmlns:dc=""
xmlns:trackback=""&
&rdf:Description
rdf:about="+Fastjson focusedCommentId=6948150#comment-6948150"
dc:identifier="+Fastjson focusedCommentId=6948150#comment-6948150"
dc:title="Inside Fastjson"
trackback:ping="" /&
&/rdf:RDF&
&rdf:RDF xmlns:rdf=""
xmlns:dc=""
xmlns:trackback=""&
&rdf:Description
rdf:about="+Fastjson focusedCommentId=6948151#comment-6948151"
dc:identifier="+Fastjson focusedCommentId=6948151#comment-6948151"
dc:title="Inside Fastjson"
trackback:ping="" /&
&/rdf:RDF&
&rdf:RDF xmlns:rdf=""
xmlns:dc=""
xmlns:trackback=""&
&rdf:Description
rdf:about="+Fastjson focusedCommentId=6948778#comment-6948778"
dc:identifier="+Fastjson focusedCommentId=6948778#comment-6948778"
dc:title="Inside Fastjson"
trackback:ping="" /&
&/rdf:RDF&
本问题标题:
本问题地址:
温馨提示:本问题已经关闭,不能解答。
暂无合适的专家
&&&&&&&&&&&&&&&
希赛网 版权所有 & &&}

我要回帖

更多关于 有道高人来指点 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信