public class FastJson2Demo {
/**
* 字符集
*/
private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
/**
* 字符串长度,此处减去1M的字符
*/
private static final long STRING_LENGTH = 63L * 1024L * 1024L;
public static void main(String[] args) {
JSONObject jsonObject = new JSONObject();
System.out.println(STRING_LENGTH);
// 生成64 * 1024 * 1024长度的字符串
Random random = new Random();
StringBuilder largeStringBuilder = new StringBuilder();
for (long i = 0; i < STRING_LENGTH; i++) {
int index = random.nextInt(CHARACTERS.length());
largeStringBuilder.append(CHARACTERS.charAt(index));
}
String largeString = largeStringBuilder.toString();
jsonObject.put("largeString", largeString);
String jsonString = jsonObject.toJSONString();
}
}
输出&错误信息:
66060288
Exception in thread "main" java.lang.OutOfMemoryError: try enabling LargeObject feature instead
at com.alibaba.fastjson2.JSONWriterUTF16.ensureCapacity(JSONWriterUTF16.java:1998)
at com.alibaba.fastjson2.JSONWriterUTF16.write(JSONWriterUTF16.java:3043)
at com.alibaba.fastjson2.JSONObject.toString(JSONObject.java:1154)
at com.alibaba.fastjson2.JSONObject.toJSONString(JSONObject.java:1166)
at com.elkszero.FastJson2Demo.main(FastJson2Demo.java:38)
问题描述
已知: JSONObject.toJSONString()最大可支持数据为64M,JSONObject.toJSONString(JSONWriter.Feature.LargeObject)最大可支持数据为1G
问题: 实际使用时,当数据大小为上述所设置的最大可支持数的2/3时,就会发生
OOM
环境信息
重现步骤
如何操作可以重现该问题:
复现Demo: 此处复现的是64M的情况,1G的修改
STRING_LENGTH
的值和JSONObject.toJSONString()
的参数即可输出&错误信息:
期待的正确结果
在考虑属性名和JSON自身的格式所需符号的情况下,实际可用容量能够达到预设的95%~99.99%? 在仅考虑值的情况下,实际可用容量能够达到预设的90%? 不考虑极端情况。 优化: 允许自行设置最大容量(?)
相关日志输出
无
附加信息
虽然在fastjson在不启用
LargeObject
的情况下,设置的maxArraySize
为64M,但是当JsonObject的实际数据内容在传入63M(远小于64M)时,在com.alibaba.fastjson2.JSONWriterUTF16#write(com.alibaba.fastjson2.JSONObject)
方法中,虽然数据能够正常写入chars,但是却依然会发生OOM
异常。造成该问题的原因便是,数据虽然已经写入,但是在最后,需要将JSON的结束}
也写入chars中。而fastjson的写入逻辑,在每次写入前,均会扩容并校验chars数据长度。而OOM
异常便是由fastjson的扩容检验
机制所导致。 如下为fastjson扩容检验
的相关代码:如上代码中包含
最小容量/目标容量(minCapacity)
、原有容量(oldCapacity)
、新容量(newCapacity)
、最大容量(this.maxArraySize)
因为
if (newCapacity - minCapacity < 0) newCapacity = minCapacity;
的存在,也就导致了其只要扩容,那么至MAX(1.5 *原有容量(舍弃小数位), 目标容量 )
。这也就导致了如果在数据写入结束且在写入}
前的扩容之前,如果已经组装的数据长度大于44739242
即42 M 682K 682
那么就会发生OOM异常