背景

发现陷入了一个怪圈,写文章的话,感觉只有大bug或比较值得写的内容才会写,每次一写就是几千字,争取写得透彻一些,但这样,我也挺费时间,读者也未必有这么多时间看。

我想着,日常遇到的小bug、平时工作中的一些小的心得体会,都还是可以写写,这样也才是最贴近咱们作为一线开发生活的,也不必非得是个完整且深入的主题,因此,准备搞一个专门的标签:点滴记录Coding之路来记录这些。

ok,咱们开始,最近,手下开发小哥去帮忙做一个其他组的项目,但遇到一些解决不了的问题就会找我帮忙看。最近来问我了一个问题,说是他有个接口,调用会报内存溢出,在本机就能复现,不知道咋回事。

上下文

接口代码如下:

在一个for循环里面,会去执行sql,查询数据库记录,存到dataList这个列表中,然后序列化为json,这里呢,他们使用的是fastjson。

他调用接口给我演示了下,上面代码不是个循环嘛,跑着跑着就报错了,报错的栈大概如下(这个栈来自网上,问题类似):

Exception in thread "pool-4-thread-1" java.lang.OutOfMemoryError
at com.alibaba.fastjson2.JSONWriterUTF16.writeNameRaw(JSONWriterUTF16.java:561)
at com.alibaba.fastjson2.writer.FieldWriterImpl.writeFieldName(FieldWriterImpl.java:143)
at com.alibaba.fastjson2.writer.ObjectWriter_3.write(Unknown Source)
at com.alibaba.fastjson2.writer.ObjectWriterImplList.write(ObjectWriterImplList.java:278)
at com.alibaba.fastjson2.JSON.toJSONString(JSON.java:1757)
.....
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)

排查

刚看到这个,也没啥思路,一开始还以为是内存、gc之类的问题,看了会后,决定在报错的地方打断点看下,到底为啥报这个。

我这边debug了两圈后,发现都是走到如下位置的时候报错:

这个函数,大概就是,在初步序列化对象为字符串后,要计算字符串的长度,然后看看这个长度能不能写入到底层JsonWriter的字符数组中(会比较字符串的长度和JsonWriter中数组的长度),如果JsonWriter中数组长度过小,这里就要触发扩容。

而扩容前,如果发现要扩容的大小大于maxArraySize(一个配置项),就会抛这个内存溢出的溢出,并不是真的发生了内存溢出。

当时debug的时候,看到maxArraySize大概是60w多,大概就是60多m大小。当时就很纳闷,是不是查出来的数据太大了,不然即使扩容啥的,也不可能大于60M,后面果然看到数据竟然达到了几十M大小,由于这个系统我也没参与,这块业务合不合理就不管了,解决问题就行。

然后我就看了下,maxArraySize赋值的地方,看看这个能不能改大点,改大了就没事了。

protected final int maxArraySize;

    protected JSONWriter(Context context, Charset charset) {
this.context = context;
this.charset = charset;
this.utf8 = charset == StandardCharsets.UTF_8;
this.utf16 = charset == StandardCharsets.UTF_16; quote = (context.features & Feature.UseSingleQuotes.mask) == 0 ? '"' : '\''; // 64M or 1G
maxArraySize = (context.features & LargeObject.mask) != 0 ? 1073741824 : 67108864;
}

这边果然看到,有个注释,64M OR 1G,果然,是个配置项,看起来,这个配置项是受LargeObject这个控制的。

一开始,我以为这个是com.alibaba.fastjson.serializer.SerializerFeature里的枚举项,结果并不是,没发现是JsonWriter的配置项:

com.alibaba.fastjson2.JSONWriter.Feature

知道是配置项了,问题是怎么配置呢?仔细看了各个方法,都不能传这种JsonWriter的枚举啊

后边,看了半天,发现这个方法可以传JsonWriter的feature:

问题是,这个defaultFeatures是int,32位整数,每个bit代表一个特性,也就是说,我得自己计算将LargeObject这个bit置为1后,整个int的值。

大家看这个feature的值:

// 十进制为:8589934592, 二进制为:001000000000000000000000000000000000
LargeObject(1L << 33),

我就根据这个,自己把这个bit设为1,然后算了个值出来,结果,跟我说,超过了int的范围,导致我没法传参进去。

解决

我都服了,然后开始在网上看看有没有类似的问题,结果只找到了一篇文章。

https://blog.csdn.net/m0_68736501/article/details/132078314

解决办法是说,升级jar包版本到2.0.16,里面有个方法,可以传JsonWriter的Feature枚举值进去:

JSON.toJSONString(t, JSONWriter.Feature.WriteClassName, JSONWriter.Feature.LargeObject).getBytes(DEFAULT_CHARSET);

结果我看了我们版本,都2.0.19了,版本比他还高,结果没看到这个方法。服了,难道高版本还把这个方法删了?

然后小伙子看我忙,就说他回去再研究研究,我说行,我也网上查下。

后边也找到篇文章,让他试试:https://www.exyb.cn/news/show-5352725.html,他没说有没有效果,但是过了一阵,他跟我说,知道问题了。

行吧,我给大家梳理下结论,我们的pom引入的依赖是:

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.19</version>
</dependency>

这个内部其实还依赖了另外的jar:

<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2-extension</artifactId>
</dependency>

而上面的这个,又依赖了:

<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>

差不多,就是下图这样的关系:

然后,导致我们项目中,其实有两个JSON类:

com.alibaba.fastjson2.JSON;  位于fastjson2-2.0.19.jar
com.alibaba.fastjson.JSON; 位于fastjson-2.0.19.jar

而之前我们导入的是下面那个,也就是传统的com.alibaba.fastjson.JSON,里面就是没法传JsonWriter的Feature枚举的,只有上面那个才有:

com.alibaba.fastjson2.JSON#toJSONString(java.lang.Object, com.alibaba.fastjson2.JSONWriter.Feature...)
/**
* Serialize Java Object to JSON {@link String} with specified {@link JSONReader.Feature}s enabled
*
* @param object Java Object to be serialized into JSON {@link String}
* @param features features to be enabled in serialization
*/
static String toJSONString(Object object, JSONWriter.Feature... features) {

所以,剩下的事情,简单了,修改import的类为com.alibaba.fastjson2.JSON即可,然后序列化时传入feature:

String previewDataJson = JSON.toJSONString(dataList,LargeObject);

问题解决。

结论

新项目建议还是用jackson算了,当然了,这个项目也不是我主导,而且都开发快完成了,就这样吧,一般大问题也没有,有就再改吧。

FastJson不成想还有个版本2啊:序列化大字符串报错的更多相关文章

  1. wcf序列化大对象时报错:读取 XML 数据时,超出最大

    错误为: 访问服务异常:格式化程序尝试对消息反序列化时引发异常: 尝试对参数 http://tempuri.org/ 进行反序列化时出 错: request.InnerException 消息是“反序 ...

  2. Atitit mac os 版本 新特性 attilax大总结

    Atitit mac os 版本 新特性 attilax大总结 1. Macos概述1 2. 早期2 2.1. Macintosh OS (系统 1.0)  1984年2 2.2. Mac OS 7. ...

  3. pathinfo()在php不同版本中对于对多字节字符处理的不同结果

    phpinfo()函数在处理路径时,在php的低版本中无法处理多字节字符,这里测试的是php5.3和php5.6 的区别 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ...

  4. 调试接口,返回的json数据,我定义了一个类,用来序列化,其中有一个字段定义为string 然后序列化的时候报错

    调试接口,返回的json数据,我定义了一个类,用来序列化,其中有一个字段定义为string 然后序列化的时候报错 在需要解析的类型类上加上声明 eg:

  5. java分享第十三天(fastjson生成和解析json数据,序列化和反序列化数据)

     fastjson简介:Fastjson是一个Java语言编写的高性能功能完善的JSON库.fastjson采用独创的算法,将parse的速度提升到极致,超过所有json库,包括曾经号称最快的jack ...

  6. fastjson生成和解析json数据,序列化和反序列化数据

    本文讲解2点: 1. fastjson生成和解析json数据 (举例:4种常用类型:JavaBean,List<JavaBean>,List<String>,List<M ...

  7. c# json序列化 意外字符i 意外字符ï 解决方案

    今天使用DataContractJsonSerializer遇到了这个问题 这是个蛋疼的问题,折腾了我好久,反复检查对象和json字符串,没发现什么问题,而且错误提示还看走眼了,是ï不是i 现公布解决 ...

  8. mongodb不同版本之间有很大的差异

    今天主要说下我为了给mongodb数据库添加authorization,大家应该知道,mongo默认是无auth运行的.这可能是方便小伙伴学习命令吧. 由于之前发布的一个项目,在亚马逊的云上,处于内部 ...

  9. JDK版本过高,导致Eclipse报错

    1.JDK版本如果比较高,而使用的eclipse版本比较低,导致在eclispe中不能识别而报错.   2.点击Attach Source添加rt.jar后,又出现如下错误 3.这样的错误就是由于ec ...

  10. 1.1.6版本Druid连接MSSQLServer 2008 R2报错The query timeout value -1 is not valid. #2210

    https://github.com/alibaba/druid/releases/tag/1.1.8问题已修复,请使用新版本 xhhwc commented on 21 Dec 2017 1.1.6 ...

随机推荐

  1. 2021-08-21:给定一个数组arr,长度为N > 1,从中间切一刀,保证左部分和右部分都有数字,一共有N-1种切法,如此多的切法中,每一种都有:绝对值(左部分最大值 – 右部分最大值)。返回最大

    2021-08-21:给定一个数组arr,长度为N > 1,从中间切一刀,保证左部分和右部分都有数字,一共有N-1种切法,如此多的切法中,每一种都有:绝对值(左部分最大值 – 右部分最大值).返 ...

  2. pages.json 文件:自定义导航栏

    自定义导航栏使用注意 当navigationStyle设为custom或titleNView设为false时,原生导航栏不显示,此时要注意几个问题: 非H5端,手机顶部状态栏区域会被页面内容覆盖.这是 ...

  3. annotate()使用聚合计数、求和、平均数 raw()执行原生的SQL

    annotate()使用聚合计数.求和.平均数  raw()执行原生的SQL # 按老师分组,求课程的销量 Course.objects.values('Teacher').annotate(vol= ...

  4. 【GiraKoo】Could NOT find PkgConfig (missing: PKG_CONFIG_EXECUTABLE)

    [解决]Could NOT find PkgConfig (missing PKG_CONFIG_EXECUTABLE) 环境 Ubuntu 22.04 现象 在编写CMakeLists.txt时,调 ...

  5. TLS 加速技术:Intel QuickAssist Technology(QAT)解决方案

    作者:vivo 互联网服务器团队- Ye Feng 本文介绍了 Intel QAT 技术方案,通过Multi-Buffer技术和QAT硬件加速卡的两种方式实现对TLS的加速 一.背景 当前 TLS 已 ...

  6. PHP中的AMQP类

    PHP中的AMQP类 标签(空格分隔): php,amqp 官网地址:http://docs.php.net/manual/da/book.amqp.php AMQPConnection AMQPCo ...

  7. # 代码随想录算法训练营Day4|24.两两交换链表中的节点 19.删除链表的倒数第N个节点 面试题02.07.链表相交 142.环形链表Ⅱ

    24.两两交换链表中的节点 题目链接:24.两两交换链表中的节点 总体思路: 两两交换链表中的节点使用虚拟头节点可以更方便地进行交换,这样头节点和普通节点可以以同一种方式进行. 虚拟头结点的建设代码: ...

  8. Godot 4.0 遮罩一个2D物体,使其部分显示

    本文针对Godot 4.0. 我也查到了Godot 3.5如何实现遮罩,见这个链接 https://ask.godotengine.org/3031/how-do-i-mask-a-sprite 由于 ...

  9. CF1608F MEX counting

    题意 给定 \(n, k\) 和序列 \(b_{1\dots n}\),计数序列 \(a_{1\dots n}\) 使得 \(\forall i \in [1, n], \operatorname{m ...

  10. 云储存选择做Hexo博客图床(腾讯云、七牛云、网易云)

    前言 博客里需要添加很多图片作为内容的补充,但是把图片放在本地博客文件夹里,上传到网上后,加载这些图片就是一个很大的问题,他们会拖累网页加载的速度,所以建议把图片放图床里,通过外链来访问和加载这些图片 ...