一次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运行时堆内存监测:
请添加图片描述

posted @   有锦  阅读(16)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示