一次OOM异常解决
问题:操作1000条数据库查出的数据导致的OOM
JDK版本:1.8
问题代码:
//list里有一千条数据
List<Map> listMap = dao.select("xxx");
String otherText = null;
if (listMap != null && listMap.size() > 0) {
//遍历结果,获取map中的value将其转化成json并拼接
for (Map map : listMap) {
if (map != null) {
if (otherText == null) {
otherText = JsonObjectUtils.converObjectTojson(map.containsKey("xxxx") ? (String) map.get("xxxx") : null);
} else if (otherText != null && StringUtils.isNotBlank(String.valueOf(map.get("xxxx")))) {
//OOM --- 执行后堆内存占用骤增
otherText = JsonObjectUtils.converObjectTojson(otherText) + "," + JsonObjectUtils.converObjectTojson(map.get("xxxx"));
}
}
}
}
异常提示:
OOM后提示的是 Java heap space --由此基本可以判断出现OOM的区域是jvm堆,堆内存储的是Java实例,那么可以初步猜测导致出现OOM的几种情况
1、实例创建时jvm堆无法申请足够的内存
2、无法GC的实例过多导致内存溢出
使用内存插件VisualVM Launcher查看执行过程中JVM内存区域的情况:
可以看到堆内存在短时间内骤增近4G,堆的使用量也瞬间超过2G
而笔者本地的内存不足以支持堆的内存扩展 因此抛出了OOM异常;
查看问题代码:
JsonObjectUtils.converObjectTojson()
是对ObjectMapper.writeValueAsString(Object value)封装的一个工具类
writeValueAsString:
public String writeValueAsString(Object value)
throws JsonProcessingException
{
// alas, we have to pull the recycler directly here...
SegmentedStringWriter sw = new SegmentedStringWriter(_jsonFactory._getBufferRecycler());
try {
_configAndWriteValue(_jsonFactory.createGenerator(sw), value);
} catch (JsonProcessingException e) { // to support [JACKSON-758]
throw e;
} catch (IOException e) { // shouldn't really happen, but is declared as possibility so:
throw JsonMappingException.fromUnexpectedIOE(e);
}
return sw.getAndClear();
}
每调用一次writeValueAsString都会创建一个SegmentedStringWriter对象
SegmentedStringWriter:
public SegmentedStringWriter(BufferRecycler br)
{
super();
_buffer = new TextBuffer(br);
}
每创建一个SegmentedStringWriter对象都会同时创建一个TextBuffer对象
相当于每次调用JsonObjectUtils.converObjectTojson这个方法都会创建两个对象
代码中循环一千次,相当于一次请求在这个循环中就创建两千个对象,加上请求前后的创建使得内存消耗瞬间提升加上操作系统给每个进程分配的内存空间是有限的导致OOM
优化代码:
List<Map> listMap = dao.select("xxx");
String otherText = null;
if (listMap != null && listMap.size() > 0) {
//遍历结果,获取value直接拼接
for (Map map : listMap) {
if (map != null) {
if (otherText == null) {
otherText = map.containsKey("xxxx") ? (String) map.get("xxxx") : null;
} else if (otherText != null && StringUtils.isNotBlank(String.valueOf(map.get("xxxx")))) {
otherText = otherText + "," + map.get("xxxx");
}
}
}
}
//根据","拆分字符串将每个单元转成json最后再拼接成字符串
if (otherText.contains(",")) {
otherText = Arrays.asList(otherText.split(",")).stream().map(str -> JsonObjectUtils.converObjectTojson(str)).collect(Collectors.joining(","));
}
优化后的JVM运行时堆内存监测:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南