`
wenshao
  • 浏览: 271679 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

吹牛:我写的JSONParser可能是这个星球上最快了(Java)

阅读更多
我拥有多年手写Parser的经验,在以前,编写过多个Parser,有SQL的Parser,也有JavaScript的Parser,也有Java的Parser。

在最近的项目中,也需要使用JSON,其中client部分不希望存在依赖,所以就写了一个JSON     Parser。最初是直接使用SimpleJSON的,因为其代码少,接口简洁。一个同事说,SimpleJSON存在性能问题,噢,我想,那算了,我自己写一个吧,JSON语法这么简单,对于有“多年行医经验”(来自典故《日本兵与老中医》)的我,自然不是困难的事情,也就是一个晚上就写完了,应用于项目中,测试结果,网络协议的性能提升了两倍,从原来的80,000 TPS提升到了240,000 TPS。顺带说一个花絮,周六晚上加班写完的,直接就应用到项目中了,虽然有TestCase,但还是有一个地方没注意好,出现了一个Bug,星期一还影响了测试工作,早会的时候被同事血泪控诉。

我跟朋友炫耀了一下,朋友说,为啥不用jackson,jackson速度很快。好吧,我就和jackson做了一个性能比较,在大多数场景都是比jackson性能好很多,但是在某一个场景,竟然比jackson稍慢。我不服气啊,因为一看就觉得jackson的Parser写得不够专业,没理由比我这种多年经验的老鸟快的。跟踪其实现代码找到了其优化的办法,觉得那是取巧,不屑于使用。于是优化一下我写的Parser,优化的结果自然是全面超越它。好吧,废话少说,直接上结果。

场景1:
[{"S":321061,"T":"GetAttributeResp"},{"ERROR":null,"TS":0,"VAL":{"SqlList":[{"BatchSizeMax":0,"BatchSizeTotal":0,"ConcurrentMax":1,"DataSource":"jdbc:wrap-jdbc:filters=default,encoding:name=ds-offer:jdbc:mysql://100.10.10.10:8066/xxxx","EffectedRowCount":0,"ErrorCount":0,"ExecuteCount":5,"FetchRowCount":5,"File":null,"ID":2001,"LastError":null,"LastTime":1292742908178,"MaxTimespan":16,"MaxTimespanOccurTime":1292742668191,"Name":null,"RunningCount":0,"SQL":"SELECT @@SQL_MODE","TotalTime":83}]}}] 

fast-json : 8,608,334 (我写的parser)
jackson : 17,172,500
simple-json : 36,258,008


场景2:
{"S":321061,"T":"GetAttributeResp"}

fast-json : 1,028,024
jackson : 6,673,850
simple-json : 20,294,257

场景3:
引用
{"name":null,"flag":true}

fast-json : 842,032
jackson : 7,284,100
simple-json : 20,553,188

场景4:
[-5.041598256063065E-20,-7210028408342716000]

fast-json : 1,338,822
jackson : 10,000,239
simple-json : 20,202,679

场景5:
-6470204979932713723

fast-json : 389,004
jackson : 5,405,129
simple-json : 20,418,252

以上的结果都是执行一千次的时间总和,单位是nano。连续执行测试50次,取最后一次的结果,让JVM能够充分优化。JVM的参数是-server。

结论,越简单的对象,偶写的parser(fast-json)性能越好。如场景3,都快差不多100倍了。

好了,吹牛结束啦,有什么后续动作呢?有同事建议开源,嗯,这个建议挺好的,我喜欢用开源的东西,也希望对开源有所贡献,我整理好代码之后,就会开源出来。

注明,我写的Parser是能通过JSON官方的兼容性测试的,不是缺乏功能的残废!

----------------------------------------------

svn已经能够外网访问:
http://119.38.217.15/svn/fastjson/trunk/
(注意,IP地址在2011-1-10修改过)

域名申请的事情还在进行中,还有很多事情要做。最终这是一个类似taobao code(淘蝌蚪)的开源平台,我们的名字叫做Open Sesame。

分享到:
评论
315 楼 vb2005xu 2011-01-12  
看你的代码 真是对我JAVA底子的 一次全新的学习 呜呜呜
314 楼 wenshao 2011-01-12  
luffyke 写道
关于循环引用,jaxb有一个方法就是当遇到循环引用时,执行一个方法,用new 一个temp obj代替this返回。。。断开循环引用。。。


你的建议不错,遇到循环饮用时,忽略处理,考虑考虑。。。 
313 楼 luffyke 2011-01-11  
关于循环引用,jaxb有一个方法就是当遇到循环引用时,执行一个方法,用new 一个temp obj代替this返回。。。断开循环引用。。。
312 楼 wenshao 2011-01-11  
优化了字符串序列化性能,SerializeWriter直接提供序列化JSON字符串的方法,代码如下:
private final static boolean[] specicalFlags = new boolean[(int) '\\' + 1];  // 用于快速判断是否特别字符
private final static char[] replaceChars = new char[(int) '\\' + 1]; // 用于特殊字符的替换处理
static {
	specicalFlags['\b'] = true;
	specicalFlags['\n'] = true;
	specicalFlags['\f'] = true;
	specicalFlags['\r'] = true;
	specicalFlags['\"'] = true;
	specicalFlags['\\'] = true;
	specicalFlags['/'] = true;
	
	replaceChars['\b'] = 'b';
	replaceChars['\n'] = 'n';
	replaceChars['\f'] = 'f';
	replaceChars['\r'] = 'r';
	replaceChars['\"'] = '"';
	replaceChars['\\'] = '\\';
	replaceChars['/'] = '/';
}

public void writeStringWithQuote(String text) throws IOException {
	if (text == null) {
		int newcount = count + 4;
		if (newcount > buf.length) {
			expandCapacity(newcount);
		}
		"null".getChars(0, 4, buf, count);
		count = newcount;
		return;
	}
	
	int len = text.length();
	int newcount = count + len + 2;
	if (newcount > buf.length) {
		expandCapacity(newcount);
	}

	count = newcount;
	
	int start = count + 1;
	int end = start + len;
	
	// 不管是否存在特殊字符,先写到buf
	buf[count] = '\"';
	text.getChars(0, len, buf, start);
	buf[newcount - 1] = '\"';
	
	// 处理特别字符,如果存在特别字符,把剩余的内容右移一位
	for (int i = start; i < end; ++i) {
		char ch = buf[i];
		if (ch < specicalFlags.length && specicalFlags[ch]) {
			newcount++;
			if (newcount > buf.length) {
				expandCapacity(newcount);
			}
			count = newcount;
			
			System.arraycopy(buf, i + 1, buf, i + 2, end - i); // 将特别字符后面的内容右移1位
			buf[i] = '\\';
			buf[++i] = replaceChars[(int) ch];
			end++;
		}
	}
	
	count = newcount;
}
311 楼 wenshao 2011-01-11  
william_ai 写道
注解的实现方式是相对比较优雅的,但是和第2种比,性能怎么样?


目前已经实现了Annotation的方式,基本没有性能影响的。
310 楼 william_ai 2011-01-11  
注解的实现方式是相对比较优雅的,但是和第2种比,性能怎么样?
309 楼 wenshao 2011-01-11  
半夜睡不着 ...

想了一些处理循环引用的办法,其中一个超级简单。

第一种,流氓粗暴的办法:
try {
	// ...
} catch (StackOverflowError e) {
	throw new JSONException("maybe circular references", e);
}

很流氓,但是很简单很有效,不会影响效率。

第二种,编写存放引用的类,如果发现引用重复,则做处理。这个类如下:

public final class JSONSerializerContext {
	public static final int DEFAULT_TABLE_SIZE = 64;

	private final Entry[] buckets;
	private final int indexMask;

	public JSONSerializerContext() {
		this(DEFAULT_TABLE_SIZE);
	}

	public JSONSerializerContext(int tableSize) {
		this.indexMask = tableSize - 1;
		this.buckets = new Entry[tableSize];
	}

	// 添加引用,如果存在则返回true,否则返回false
	public final boolean put(Object obj) { 
		final int hash = System.identityHashCode(obj);
		final int bucket = hash & indexMask;

		for (Entry entry = buckets[bucket]; entry != null; entry = entry.next) {
			if (obj == entry.object) {
				return true;
			}
		}

		Entry entry = new Entry(obj, hash, buckets[bucket]);
		buckets[bucket] = entry;

		return false;
	}

	protected static final class Entry {
		public final int hashCode;
		public final Object object;

		public Entry next;

		public Entry(Object object, int hash, Entry next) {
			this.object = object;
			this.next = next;
			this.hashCode = hash;
		}
	}
}


只需要JavaBean的序列化是调用put方法检测就可以了,应该对性能影响不大,具体数据等有空测试才能具体确定影响多大,但初步估计性能影响较小。

对于出现循环引用的情况,处理办法两种:抛异常或者忽略。如果是抛异常,还不如上面第一种方式有效,如果忽略,还不如这样写:

import com.alibaba.fastjson.annotation.JSONField;

public class Category {
	private Category parent;
	private List<Category> children = new ArrayList<Category>();

	@JSONField(serialize=false) // 标识不需要序列化
	public Category getParent() {
		return parent;
	}

	public void setParent(Category parent) {
		this.parent = parent;
	}

	public List<Category> getChildren() {
		return children;
	}

	public void setChildren(List<Category> children) {
		this.children = children;
	}
}


我在网上搜索了一下,JSON.NET也是采用Annotation标识忽略的办法来解决循环引用的。

大家给些参考意见。

308 楼 luffyke 2011-01-11  
wenshao 写道
回luffyke和qiangshao:

我还在做呢,循环检测的实现还没做完,因为今晚处理了些别的事情。

做了一半,检测的实现在这个类JSONSerializerContext中,还没加入进去,得明天晚上再搞了。

今晚优化了String的序列化性能。




呵呵,慢慢来,刚才更新了下代码,正在看呢。。。加油咯。。。
307 楼 wenshao 2011-01-10  
回luffyke和qiangshao:

我还在做呢,循环检测的实现还没做完,因为今晚处理了些别的事情。

做了一半,检测的实现在这个类JSONSerializerContext中,还没加入进去,得明天晚上再搞了。

今晚优化了String的序列化性能。



306 楼 luffyke 2011-01-10  
qiangshao说的很对,温少可以先关注一下他的那个问题。。。
305 楼 yefeng 2011-01-10  
wenshao 写道
yefeng 写道
楼主,能否简单说一下,做了哪些改进
我checkout下代码,看了一下,还是基于反射的,


是否使用反射,这不是性能的关键点,使用cglib能够提升一点性能,但是影响不大。

其中很多优化性能的办法,你可以看源代码和之前的一些讨论。

前面30页,实在太长了,能否有个总结性的地方
304 楼 wenshao 2011-01-10  
yefeng 写道
楼主,能否简单说一下,做了哪些改进
我checkout下代码,看了一下,还是基于反射的,


是否使用反射,这不是性能的关键点,使用cglib能够提升一点性能,但是影响不大。

其中很多优化性能的办法,你可以看源代码和之前的一些讨论。
303 楼 wenshao 2011-01-10  
接到通知,公网访问的IP地址变更,新的访问地址如下:

http://119.38.217.15/svn/fastjson/trunk/
302 楼 yefeng 2011-01-10  
楼主,能否简单说一下,做了哪些改进
我checkout下代码,看了一下,还是基于反射的,
301 楼 qiangshao 2011-01-10  
qiangshao 写道
wenshao 写道
举例:
User user = new User();
List<User> userList = new ArrayList<User>();

String jsonString1 = JSON.toJSONStringEx(user);
String jsonString2 = JSON.toJSONStringEx(userList);

User user1 = JSON.parseObject(User.class, jsonString1 ); 
List<User> userList1 = JSON.parseArray(User.class, jsonString2 );



还有一个比较关心的是
一些字段的过滤
比如一个对象循环引用,或者这个对象非常复杂,时间使用时 那些字段根本不需要。
这个功能是非常重要的,对提升性能来说也是一个突破点,
最好能提供模板.


wenshao 写道
sdh5724 写道
wenshao 写道
lqixv 写道
这个好像对内存要求太高了,我开发用的机器,全部内存才 2G,使用了你的这个 fastjson 后,总是内存溢出,没法跑。

Caused by: java.lang.StackOverflowError
at com.alibaba.fastjson.serializer.FieldSerializer.writePrefix(FieldSerializer.java:54)
at com.alibaba.fastjson.serializer.StringFieldSerializer.write(StringFieldSerializer.java:29)
at com.alibaba.fastjson.serializer.FieldSerializer.write(FieldSerializer.java:64)
at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:162)
at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:156)
at com.alibaba.fastjson.serializer.ObjectFieldSerializer.write(ObjectFieldSerializer.java:47)
at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:162)
at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:156)
at com.alibaba.fastjson.serializer.ListSerializer.write(ListSerializer.java:71)
at com.alibaba.fastjson.serializer.ListFieldSerializer.write(ListFieldSerializer.java:31)
at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:162)
。。。。。。


这个是BUG,死循环了,不是对内存要求高。

能够把序列化的对象结构提供出来么?



程序的健壮性不是说不支持就好的, 要告诉客户发生了才是关键。


在考虑底成本实现循环引用的检测的办法,可能可行,这两天有空就试一下!


这个我早就提出来了,不知道你有没有看到,实现这个功能非常有必要.

300 楼 wenshao 2011-01-10  
sdh5724 写道
wenshao 写道
lqixv 写道
这个好像对内存要求太高了,我开发用的机器,全部内存才 2G,使用了你的这个 fastjson 后,总是内存溢出,没法跑。

Caused by: java.lang.StackOverflowError
at com.alibaba.fastjson.serializer.FieldSerializer.writePrefix(FieldSerializer.java:54)
at com.alibaba.fastjson.serializer.StringFieldSerializer.write(StringFieldSerializer.java:29)
at com.alibaba.fastjson.serializer.FieldSerializer.write(FieldSerializer.java:64)
at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:162)
at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:156)
at com.alibaba.fastjson.serializer.ObjectFieldSerializer.write(ObjectFieldSerializer.java:47)
at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:162)
at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:156)
at com.alibaba.fastjson.serializer.ListSerializer.write(ListSerializer.java:71)
at com.alibaba.fastjson.serializer.ListFieldSerializer.write(ListFieldSerializer.java:31)
at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:162)
。。。。。。


这个是BUG,死循环了,不是对内存要求高。

能够把序列化的对象结构提供出来么?



程序的健壮性不是说不支持就好的, 要告诉客户发生了才是关键。


在考虑底成本实现循环引用的检测的办法,可能可行,这两天有空就试一下!
299 楼 sdh5724 2011-01-10  
wenshao 写道
lqixv 写道
这个好像对内存要求太高了,我开发用的机器,全部内存才 2G,使用了你的这个 fastjson 后,总是内存溢出,没法跑。

Caused by: java.lang.StackOverflowError
at com.alibaba.fastjson.serializer.FieldSerializer.writePrefix(FieldSerializer.java:54)
at com.alibaba.fastjson.serializer.StringFieldSerializer.write(StringFieldSerializer.java:29)
at com.alibaba.fastjson.serializer.FieldSerializer.write(FieldSerializer.java:64)
at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:162)
at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:156)
at com.alibaba.fastjson.serializer.ObjectFieldSerializer.write(ObjectFieldSerializer.java:47)
at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:162)
at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:156)
at com.alibaba.fastjson.serializer.ListSerializer.write(ListSerializer.java:71)
at com.alibaba.fastjson.serializer.ListFieldSerializer.write(ListFieldSerializer.java:31)
at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:162)
。。。。。。


这个是BUG,死循环了,不是对内存要求高。

能够把序列化的对象结构提供出来么?



程序的健壮性不是说不支持就好的, 要告诉客户发生了才是关键。







298 楼 wenshao 2011-01-10  
rtm 写道
可以默认序列化的层级,比如默认3层,当前对象算一层,对象里面的对象属性或者容器算2层。当然要允许序列化全部。一般3层肯定够用了,超过3层可能本身就存在问题,为什么要序列化这么多数据到页面。死循环太危险了,这也是其他JSON工具的通病。


3层是不够的,循环引用的检测成本较高,就算加入支持也不能够作为缺省实现。
297 楼 vb2005xu 2011-01-10  
对于时间 我觉得还是 输出 时间戳 更好 无论怎么弄 更好的转换
296 楼 rtm 2011-01-10  
可以默认序列化的层级,比如默认3层,当前对象算一层,对象里面的对象属性或者容器算2层。当然要允许序列化全部。一般3层肯定够用了,超过3层可能本身就存在问题,为什么要序列化这么多数据到页面。死循环太危险了,这也是其他JSON工具的通病。

相关推荐

    elasticdump报错

    6: v8::internal::Handle&lt;v8::internal::String&gt; v8::internal::JsonParser&lt;false&gt;::SlowScanJsonString&lt;v8::internal::SeqTwoByteString, unsigned short&gt;(v8::internal::Handle&lt;v8::internal::String&gt;, int, int)...

    JSONParser所需jar包 json simple jar包

    JSONParser是Java编程语言中用于解析JSON(JavaScript Object Notation)数据的一种工具。JSON是一种轻量级的数据交换格式,它基于ECMAScript的一个子集,采用完全独立于语言的文本格式,易于人阅读和编写,同时也...

    JSONParser.java

    json解析的示例

    JsonParser_java:纯java写的JSON解析器-eclipse工程

    "JsonParser_java"项目就是一个基于纯Java实现的JSON解析器,适用于Eclipse开发环境。 这个项目的核心目标是提供一个轻量级的解决方案,让用户能够理解和控制JSON解析过程。在Java中,这通常涉及读取JSON字符串,将...

    Kotlin-JsonParser:Kotlin-JsonParser是用于JSON解析的Kotlin库。 它可以将JSON字符串解析为Kotlin Object或以一种简单的方式进行反向

    Kotlin-杰森·帕瑟(Kotlin-JsonParser) Kotlin-JsonParser是用于JSON解析的Kotlin库。 它可以将JSON字符串解析为Kotlin Object或以一种简单的方式反向。... implementation 'com.tomatobean:jsonparser:1.0.

    matlab开发-JSONParser

    对于`JSONParser`这个项目,`license.txt`可能指定了该代码的许可协议,例如MIT、Apache 2.0或者GPL等。这些协议规定了代码的使用、修改和分发的规则,确保了开源软件的合规使用。 在实际应用中,理解JSONParser的...

    开源项目-buger-jsonparser.zip

    在Go语言中,标准库`encoding/json`提供了JSON的解析和编码功能,但本项目buger/jsonparser作为替代方案,旨在提供更快的性能,这对于处理大量JSON数据的程序尤其重要。 JSON(JavaScript Object Notation)是一种...

    jsonparser.zip

    - JSON-P (JSR 353) 和 JSON-B (JSR 374):Java EE标准,提供了API进行JSON处理。 2. JSON解析的基本步骤: - 创建JSON对象或数组:根据JSON字符串,使用解析器创建相应的JSONObject或JSONArray对象。 - 读取...

    JSON转化工具类 JSONParser.zip

    JSON转化工具类 JSONParser ,iOS数据转化组件 (JSONParser),支持JSON字符串、模型、字典、模型数组、字典数组之间...

    Android代码-JsonParser

    JsonParser Json parsing tool can flexibly configure Import add to build.gradle,${latest.version} is dependencies { compile 'com.blakequ.parser:parser:${latest.version}' } maven ...

    JSONParser:在 Java 对象上使用 Gson (toJsonfromJson)

    JSON解析器在 Java 对象上使用 Gson (toJson / fromJson) 展示了 Gson 库的简单使用,将 Java 对象转换为 JSON 格式,反之亦然; 将 JSON 字符串转换为 Java 对象。

    JSONParser-master_websocket_源码.rar

    JSONParser-master_...这个压缩包可能包含了实现这些功能的源代码,具体细节需要解压后查看代码来深入理解。如果你需要开发基于WebSocket的实时应用,并且涉及到JSON数据的交换,这个源码将是一个很好的学习资源。

    java程序B站json字幕转srt

    9. **面向对象编程**:这个程序很可能采用了面向对象的设计模式,将不同的功能封装到不同的类中,如`JsonParser`用于解析JSON,`SrtGenerator`用于生成SRT,这样可以使代码更清晰、可维护性更强。 10. **软件工程...

    JSONParser-master_websocket_源码.zip

    结合JSONParser和WebSocket,这个项目可能提供了一个解决方案,用于通过WebSocket实时传输和解析JSON数据。例如,服务器可以发送JSON格式的消息,客户端接收到消息后,使用JSONParser解析成对象,然后进行相应的业务...

    JSONParser:JSON解析器

    - `JSONParser` 类:虽然名为`JSONParser`,但这个库实际上并没有提供一个单独的解析器类。解析JSON通常通过`JSONObject`或`JSONArray`的构造函数完成,它们可以接受一个字符串参数并自动解析。 2. **...

    jsonParser

    这个库可能是一个开源项目,用于帮助开发者高效地解析和生成JSON格式的数据。 JSON格式本身具有易读性,它由键值对组成,键用引号括起来,值可以是字符串、数字、布尔值、数组、对象或者null。在C++中,解析JSON...

    JsonParser.java

    利用java反射机制实现的json与java对象互相转化的工具,只暴露了两个空开的接口,其他接口都是私有的。

    PyPI 官网下载 | jsonparser-1.0.tar.gz

    Python的内置`json`模块提供了基本的JSON编码和解码功能,但`jsonparser`可能提供额外的特性或优化,例如更快的解析速度、更丰富的错误处理机制或者支持特定的JSON扩展。 **安装与使用** 要安装`jsonparser`,首先...

Global site tag (gtag.js) - Google Analytics