记一次生产大对象导致的OOM让架构师连夜排查解决

为何半夜告警电话狂打不停,为何上线用户投诉不断,是道德的沦丧还是人性的扭曲,NO,是代码的缺陷。

Java8的JVM内存管理中,大对象生成直接放入老年代的,当老年代空间不足,就会进行FullGC,频繁的生成大对象,会进行频繁的FullGC,甚至直接OOM导致应用宕机。

有时候一段平平无奇的代码,看似温柔,实则暗藏着巨大隐患,这时候算法显得格外的重要,这也是为什么算法是一些大厂招人必备考核项目的原因之一。如何去用最短的时间,最少的空间去使整个产品更加健壮流畅。

在某个场景中需要计算出时长,已知开始时间,结束时间,去除的时间,去除的时间包含多个时间段,比如2019-11-20 00:00:00到2020-12-01 00:00:00,需要去除掉2018-12-20 05:09:00到2019-12-19 09:09:00,2020-04-20 11:00:00到2020-06-23 00:08:00,2020-11-19 00:55:00到2020-12-20 00:00:00,以及2021-12-20 00:00:00到2021-11-19 07:45:00这些时间段,需要计算出在开始和结时间内,除去 需要去除的时间段的秒数,剩余多少秒数。

最初的代码,也就是造成OOM的代码如下,用一个set集合A来保存开始到结束时间内的秒数,用另一个set集合B来保存去除时间段的秒数,然后A除去B中的元素,返回A的size。

看似功能上没啥毛病,但是如果当时长过大的时候,就需要一个很大的数组来存放元素,大对象直接放入老年代,用户量高峰期、后台任务高峰期,就会生成大量的大数组,导致老年代空间不足。

public static int countSecond(long start, long end, List<Map<String, Long>> excludes) {
        HashSet<Long> seconds = new HashSet<Long>();
        for(long second=start; second<=end; second++) {
            seconds.add(second);
        }
        HashSet<Long> excludeSeconds = new HashSet<Long>();
        if(excludes != null && excludes.size()>0) {
            for (Map<String, Long> exclude : excludes) {
                Long exStart = exclude.get("start");
                Long exEnd = exclude.get("end");
                for (long second = exStart; second <= exEnd; second++) {
                    excludeSeconds.add(second);
                }
            }
        }
        if(excludeSeconds.size() > 0) seconds.removeAll(excludeSeconds);
        return seconds.size();
    }

该代码问题暴露的时间恰巧是半夜凌晨,运维重启无效,告警一直持续,联系到开发定位,定位到该段代码,连夜紧急修复并上线,第一修复版本,先把秒数除以/60,得到分钟数,最后结果再乘*60并返回,这样容量就能够缩小60倍,当时的数据缩小60倍之后,当同样的集群资源下能够正常运行。

public static int countSecond(long start, long end, List<Map<String, Long>> excludes) {
        HashSet<Long> seconds = new HashSet<Long>();
        start = start / 60;
        end = end / 60;
        for(long second=start; second<=end; second++) {
            seconds.add(second);
        }
        HashSet<Long> excludeSeconds = new HashSet<Long>();
        if(excludes != null && excludes.size()>0) {
            for (Map<String, Long> exclude : excludes) {
                Long exStart = exclude.get("start") / 60;
                Long exEnd = exclude.get("end") / 60;
                for (long second = exStart; second <= exEnd; second++) {
                    excludeSeconds.add(second);
                }
            }
        }
        if(excludeSeconds.size() > 0) seconds.removeAll(excludeSeconds);
        return seconds.size() * 60;
    }

其实会发现第一修复版本依然还是会存在OOM的问题的,如果时长足够的话,并且除以60存在精度问题。而且这种算法的空间复杂度和时间复杂度都过于高。

通过相加减的方式直接得出,去除交集即可。

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值