背景

发现陷入了一个怪圈,写文章的话,感觉只有大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-10-16:单词拆分 II。给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的句子。

    2021-10-16:单词拆分 II.给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中.返回所有这些可能的句子. ...

  2. vue全家桶进阶之路46:Vue3 Axios拦截器

    在Vue.js 3中,使用Axios与Vue.js 2.x中类似,但是需要进行一些修改和更新,下面是Vue.js 3中Axios的定义和使用方式: 首先,你需要安装Axios和Vue.js 3.x,可 ...

  3. drf序列化器之反序列化的数据验证

    模型层 from django.db import models # Create your models here. class Manufacturer(models.Model): ## 厂商 ...

  4. 运行和编译时期资源加载的不同【vue】

    开发语言都有编译和运行两个阶段,很多时候这个也会带来许多bug 如:一个项目在开发阶段测试没有问题,然上线发布后就会有这样那样的问题,譬如说图片的加载,静态数据(js,css,json)读取错误 一 ...

  5. GroundingDINO(一种开集目标检测算法)服务化,根据文本生成检测框

    背景 最近发现一个叫GroundingDINO的开集目标检测算法,所谓开集目标检测就是能检测的目标类别不局限于训练的类别,这个算法可以通过输入文本的prompt然后输出对应的目标框.可以用来做预标注或 ...

  6. Kubernetes 研究笔记

    Kubernetes 研究笔记 在接下来的这篇笔记中,我将会介绍 Kubernetes 这一强大的容器编排工具,并学习其基本使用方法.该笔记将会被存储在https://github.com/owlma ...

  7. tryhackem_wonderland

    涉及,解密,扫描,横向移动,纵向移动 仙境 掉进兔子洞,进入仙境. 获得shell 解法一: 目录扫描 ffuf -u http://10.10.134.189/FUZZ -w /usr/share/ ...

  8. 使用CNI网络插件(calico)实现docker容器跨主机互联

    目录 一.系统环境 二.前言 三.CNI网络插件简介 四.常见的几种CNI网络插件对比 五.Calico网络之间是如何通信的 六.配置calico让物理机A上的docker容器c1可以访问物理机B上的 ...

  9. WPF中登录接口

    通过获取文本将json字符串转化成对象并做好相应的匹配 步骤 获取相应的文本 json字符串转化对象 json转化成实体类 参考链接:https://www.sojson.com/json2cshar ...

  10. 解决Mysql 5.7 不能插入中文的问题

    问题的解决方案 问题描述 : 在学习DML插入中文数据时 , 发现出现了以下问题 -- 插入数据 insert into tea (id , name) values (2 , '徐凤年'); -- ...