FastJson不成想还有个版本2啊:序列化大字符串报错
背景
发现陷入了一个怪圈,写文章的话,感觉只有大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啊:序列化大字符串报错的更多相关文章
- wcf序列化大对象时报错:读取 XML 数据时,超出最大
错误为: 访问服务异常:格式化程序尝试对消息反序列化时引发异常: 尝试对参数 http://tempuri.org/ 进行反序列化时出 错: request.InnerException 消息是“反序 ...
- 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. ...
- pathinfo()在php不同版本中对于对多字节字符处理的不同结果
phpinfo()函数在处理路径时,在php的低版本中无法处理多字节字符,这里测试的是php5.3和php5.6 的区别 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ...
- 调试接口,返回的json数据,我定义了一个类,用来序列化,其中有一个字段定义为string 然后序列化的时候报错
调试接口,返回的json数据,我定义了一个类,用来序列化,其中有一个字段定义为string 然后序列化的时候报错 在需要解析的类型类上加上声明 eg:
- java分享第十三天(fastjson生成和解析json数据,序列化和反序列化数据)
fastjson简介:Fastjson是一个Java语言编写的高性能功能完善的JSON库.fastjson采用独创的算法,将parse的速度提升到极致,超过所有json库,包括曾经号称最快的jack ...
- fastjson生成和解析json数据,序列化和反序列化数据
本文讲解2点: 1. fastjson生成和解析json数据 (举例:4种常用类型:JavaBean,List<JavaBean>,List<String>,List<M ...
- c# json序列化 意外字符i 意外字符ï 解决方案
今天使用DataContractJsonSerializer遇到了这个问题 这是个蛋疼的问题,折腾了我好久,反复检查对象和json字符串,没发现什么问题,而且错误提示还看走眼了,是ï不是i 现公布解决 ...
- mongodb不同版本之间有很大的差异
今天主要说下我为了给mongodb数据库添加authorization,大家应该知道,mongo默认是无auth运行的.这可能是方便小伙伴学习命令吧. 由于之前发布的一个项目,在亚马逊的云上,处于内部 ...
- JDK版本过高,导致Eclipse报错
1.JDK版本如果比较高,而使用的eclipse版本比较低,导致在eclispe中不能识别而报错. 2.点击Attach Source添加rt.jar后,又出现如下错误 3.这样的错误就是由于ec ...
- 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 ...
随机推荐
- lec-4-Introduction to Reinforcement Learning
模仿学习imitation learning与RL的不同 模仿学习中需要有专家指导的信息 RL不需要访问专家信息 RL Definitions 奖励函数 马尔科夫决策链 只与上一个状态有关 目的 空间 ...
- 【Python】爬虫下载视频
Python爬虫下载视频 前言 这两天我一时兴起想学习 PS ,于是去我的软件宝库中翻出陈年已久的 PhotoshopCS6 安装,结果发现很真流畅诶! 然后去搜索学习视频,网上的视频大多浮躁,收费, ...
- .Net使用第三方onnx或ModelBuilder轻松接入AI模型
ML.Net - 开源的跨平台机器学习框架 支持CPU/GPU训练 轻松简洁的预测代码 可扩展其他的机器学习平台 跨平台 1.使用Visual Studio的Model Builder训练和使用模型 ...
- JQuery的认识和安装
jQuery 是一个 JavaScript 函数库. jQuery 是一个轻量级的"写的少,做的多"的 JavaScript 库. jQuery 库包含以下功能: HTML 元素选 ...
- 新版idea快捷键总结学习----(用于java开发模式)
选择代码区 ctrl w 如果放到以if开头的语句,可以选择if判断条件所在的代码片段 游标在单个单词下时 选择单词 在选中多个单词时,选择整个字符串 三次点击时,如果不在字符串单词下,用于选择{}内 ...
- 2023-06-03:redis中pipeline有什么好处,为什么要用 pipeline?
2023-06-03:redis中pipeline有什么好处,为什么要用 pipeline? 答案2023-06-03: Redis客户端执行一条命令通常包括以下四个阶段: 1.发送命令:客户端将要执 ...
- 深入探索C++对象模型(Inside the C++ object model) -- 摘阅笔记(关于对象 - esp 1)
Object Lessons 关于对象 在C语言中,"数据"和"处理数据的操作(函数)"是分开声明的,也就是说 ,语言本身并没有支持"数据和函数&qu ...
- JUC同步锁原理源码解析二--ReentrantReadWriteLock
JUC同步锁原理源码解析二----ReentrantReadWriteLock 1.读写锁的来源 在开发场景下,对于写操作我们为了保证原子性所以需要上锁,但是对于读操作,由于其不改变数据,只是单纯 ...
- Android Studio中SQLite的使用,主要介绍sqlite插入和读出图片(ViewBinder)的操作方法
sqlite简介 本人最近在写一个小的安卓项目,开发app过程中用到了安卓自带的sqlite.本文主要对sqlite图片操作进行介绍,其他存入文本之类的操作和普通数据库一样,众所周知,sqlite是一 ...
- 第四章 VIVIM编辑器
1. 是什么 VI 是 Unix 操作系统和类 Unix 操作系统中最通用的文本编辑器. VIM 编辑器是从 VI 发展出来的一个性能更强大的文本编辑器. 2. 一般模式 以 vi/v ...