问题:操作1000条数据库查出的数据导致的OOM
JDK版本:1.8 问题代码:
List<Map> listMap = dao.select("xxx");
String otherText = null;
if (listMap != null && listMap.size() > 0) {
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")))) {
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
{
SegmentedStringWriter sw = new SegmentedStringWriter(_jsonFactory._getBufferRecycler());
try {
_configAndWriteValue(_jsonFactory.createGenerator(sw), value);
} catch (JsonProcessingException e) {
throw e;
} catch (IOException e) {
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) {
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");
}
}
}
}
if (otherText.contains(",")) {
otherText = Arrays.asList(otherText.split(",")).stream().map(str -> JsonObjectUtils.converObjectTojson(str)).collect(Collectors.joining(","));
}
优化后的JVM运行时堆内存监测:
|