线上OOM问题排查

事故描述

运维告知线上服务每隔一段时间会重启,服务不停在fullgc,让尽快解决

排查过程

因为已经知道是在fullgc,所以直接让运维给了一下服务下线时的dump文件,分析如下图:
在这里插入图片描述
很明显能看出来其中BusinessLogUtil的对象数量合占用空间大小有异常。

代码定位

public static void sendLog(List<BusinessLogVO> bizLogVos) {
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void afterCompletion(int status) {
                    if (STATUS_COMMITTED != status) {
                        bizLogVos.forEach(bvo -> {
                            bvo.setOperResult("failed");
                        });
                    }
                    executorService.execute(() -> {
                        try {
                            getBusinessLogService().saveBusinessLog(bizLogVos);
                        } catch (Throwable e) {
                            log.error("###HRTMBusinessLogUtil.rollback{},exception:{}", GsonHelper.ToJSon(bizLogVos), e);
                        }
                    });

                }
            });
        } else {
            executorService.execute(() -> {
                try {
                    getBusinessLogService().saveBusinessLog(bizLogVos);
                } catch (Throwable e) {
                    log.error("###HRTMBusinessLogUtil.doSendAsync{},exception:{}", GsonHelper.ToJSon(bizLogVos), e);
                }
            });
        }
    }

问题主要发生在监听事务提交之后再执行saveBusinessLog
在这里插入图片描述
这段代码只是注册了一个事务提交后的事件,外层调用方是在事务里面调用的这个方法,因为外层的数据量比较大,每次遍历都会调起这个方法,提交一个事件监听,因为事务一直没有提交,事件监听的代码一直没有被执行到,bizLogVos的引用还是在的,同时外层循环又不断在new新的对象出来,最终导致OOM

// 外层类似于这样的调用
@Transactional
public void test() {
	for (int i = 0; i < 5000000; i++) {
		BusinessLogVO businessLogVO = new BusinessLogVO();
        BusinessLogUtil.sendLog(Arrays.asList(businessLogVO));
    }
}

解决方案

这段代码是为了记录用户操作记的日志,其本身不应该关注事务,调用方应该清楚的知道是否要记录日志,记录什么内容的日志;因此这里直接发注册spring事务提交后的代码去掉

executorService.execute(() -> {
	try {
		getBusinessLogService().saveBusinessLog(bizLogVos);
	} catch (Throwable e) {
		log.error("###HRTMBusinessLogUtil.rollback{},exception:{}", GsonHelper.ToJSon(bizLogVos), e);
	}
});

这个方法不再关注事务,成功还是失败由调用方确定。
也就是说,调用了之后就处理然后方法推出,bizLogVos后续没有被再次使用,进入垃圾回收阶段,不会再出现new出来的对象需要等到事务提交之后再处理。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值