背景

发现陷入了一个怪圈,写文章的话,感觉只有大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. OData WebAPI实践-兼容OData集合响应

    本文属于 OData 系列文章 引言 OData 是一个开放标准,已经在 oasis 组织标准化,因此我们可以在标准的官网查询到 OData 的标准请求与返回形式:OData JSON Format ...

  2. ABP - 缓存模块(1)

    1. 与 .NET Core 缓存的关系和差异 ABP 框架中的缓存系统核心包是 Volo.Abp.Caching ,而对于分布式缓存的支持,abp 官方提供了基于 Redis 的方案,需要安装 Vo ...

  3. 现代 C++ 性能飞跃之:移动语义

    *以下内容为本人的学习笔记,如需要转载,请声明原文链接微信公众号「ENG八戒」https://mp.weixin.qq.com/s/Xd_FwT8E8Yx9Vnb64h6C8w 带给现代 C++ 性能 ...

  4. LeetCode 双周赛 106(2023/06/10)两道思维题

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 加入知识星球提问. 往期回顾:LeetCode 单周赛第 348 场 · 数位 DP 模版学会了吗? 双周赛 106 ...

  5. Python爬虫(二):写一个爬取壁纸网站图片的爬虫(图片下载,词频统计,思路)

    好家伙,写爬虫   代码: import requests import re import os from collections import Counter import xlwt # 创建Ex ...

  6. 【Linux内核】内核源码编译

    Linux内核源码编译过程 总体流程: 下载Linux内核源码文件 安装所需工具 解压源码文件并配置 make编译源码 下载busybox 配置busybox并编译 1. Linux源码编译 http ...

  7. 2023-06-29:redis中什么是热点Key?该如何解决?

    2023-06-29:redis中什么是热点Key?该如何解决? 答案2023-06-29: 在Redis中,经常被访问的key被称为热点key. 产生原因和危害 原因 热点key问题产生的原因可以归 ...

  8. 「Python实用秘技15」pandas中基于范围条件进行表连接

    本文完整示例代码及文件已上传至我的Github仓库https://github.com/CNFeffery/PythonPracticalSkills 这是我的系列文章「Python实用秘技」的第15 ...

  9. Centos7中搭建Redis6集群操作步骤

    目录 下载安装包 解压安装装包 安装依赖 安装 创建目录 设置配置文件 创建启动服务 制作启动文件 启动并验证Redis 开放防火墙端口 创建集群 集群其他操作 注意 下载安装包 # 进入软件下载目录 ...

  10. P3574 [POI2014] FAR-FarmCraft 吐槽 + 题解

    洛谷上面的题解写的真的不太好,有很多错误,我来谈谈自己的理解. 设 \(f[i]\) 表示以 \(i\) 为根节点的子树中(包括节点 \(i\))的所有人安装好游戏所需要的时间(与下面的 \(g[i] ...