请不要在JDK7及以上用Json-lib了
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了请不要在JDK7及以上用Json-lib了,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含3382字,纯文字阅读大概需要5分钟。
内容图文
【Json-lib 介绍】
Json-lib 是以前 Java 常用的一个 Json 库,最后的版本是 2.4,分别提供了 JDK 1.3 和 1.5 的支持,最后更新时间是 2010年12月14日。虽然已经很多年不维护了,但在搜索引擎上搜索 "Java Json" 等相关的关键词发现好像一直还有人在介绍和使用这个库。项目官网是 http://json-lib.sourceforge.net/。
【一句话结论】
Json-lib 在通过字符串解析每一个 Json 对象时,会对当前解析位置到字符串末尾进行 substring 操作,由于 JDK7 及以上的 substring 会完整拷贝截取后的内容,所以当遇到较大的 Json 数据并且含有较多对象时,会进行大量的字符数组复制操作,导致了大量的 CPU 和内存消耗,甚至严重的 Full GC 问题。
【问题分析】
某天发现线上生产服务器有不少 Full GC 问题,排查发现产生 Full GC 时某个老接口量会上涨,但这个接口除了解析 Json 外就是将解析后的数据存储到了缓存中,遂怀疑跟接口请求参数大小有关,打日志发现确实有比一般请求大得多的 Json 数据,但也只有 1MB 左右。为了简化这个问题,编写如下的性能测试代码。
1 package net.mayswind; 2 3 import net.sf.json.JSONObject; 4 import org.apache.commons.io.FileUtils; 5 6 import java.io.File; 7 8 9 public class JsonLibBenchmark { 10 public static void main(String[] args) throws Exception { 11 String data = FileUtils.readFileToString(new File("Z:\\data.json")); 12 benchmark(data, 5); 13 } 1415 private static void benchmark(String data, int count) { 16long startTime = System.currentTimeMillis(); 1718for (int i = 0; i < count; i++) { 19 JSONObject root = JSONObject.fromObject(data); 20 } 2122long elapsedTime = System.currentTimeMillis() - startTime; 23 System.out.println(String.format("count=%d, elapsed time=%d ms, avg cost=%f ms", count, elapsedTime, (double) elapsedTime / count)); 24 } 25 }
上述代码执行后平均每次解析需要 7秒左右才能完成,如下图所示。
测试用的 Json 文件,“...” 处省略了 34,018 个相同内容,整个 Json 数据中包含了 3万多个 Json 对象,实际测试的数据如下图所示。
{ "data": [ { "foo": 0123456789, "bar": 1234567890 }, { "foo": 0123456789, "bar": 1234567890 }, ... ] }
使用 Java Mission Control 记录执行的情况,如下图所示,可以看到分配了大量 char[] 数组。
翻看相关源码,其中 JSONObject._fromJSONTokener 方法主要内容如下所示。可以看到其在代码一开始就匹配是否为 "null" 开头。
private static JSONObject _fromJSONTokener(JSONTokener tokener, JsonConfig jsonConfig) { try { if (tokener.matches("null.*")) { fireObjectStartEvent(jsonConfig); fireObjectEndEvent(jsonConfig); returnnew JSONObject(true); } elseif (tokener.nextClean() != ‘{‘) { throw tokener.syntaxError("A JSONObject text must begin with ‘{‘"); } else { fireObjectStartEvent(jsonConfig); Collection exclusions = jsonConfig.getMergedExcludes(); PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter(); JSONObject jsonObject = new JSONObject();
...
而 matches 方法更是直接用 substring 截取当前位置到末尾的字符串,然后进行正则匹配。
public boolean matches(String pattern) { String str = this.mySource.substring(this.myIndex); return RegexpUtils.getMatcher(pattern).matches(str); }
字符串 substring 会传入字符数组、起始位置和截取长度创建一个新的 String 对象。
public String substring(int beginIndex) { if (beginIndex < 0) { thrownew StringIndexOutOfBoundsException(beginIndex); } int subLen = value.length - beginIndex; if (subLen < 0) { thrownew StringIndexOutOfBoundsException(subLen); } return (beginIndex == 0) ? this : new String(value, beginIndex, subLen); }
在 JDK7 及以上,调用该构造方法时在最后一行会复制一遍截取后的数据,这也是导致整个问题的关键所在了。
public String(char value[], int offset, int count) { if (offset < 0) { thrownew StringIndexOutOfBoundsException(offset); } if (count <= 0) { if (count < 0) { thrownew StringIndexOutOfBoundsException(count); } if (offset <= value.length) { this.value = "".value; return; } } // Note: offset or count might be near -1>>>1.if (offset > value.length - count) { thrownew StringIndexOutOfBoundsException(offset + count); } this.value = Arrays.copyOfRange(value, offset, offset+count); }
原文:https://www.cnblogs.com/mayswind/p/9222245.html
内容总结
以上是互联网集市为您收集整理的请不要在JDK7及以上用Json-lib了全部内容,希望文章能够帮你解决请不要在JDK7及以上用Json-lib了所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。