欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

记一次线上报表导出 OOM_Java head space

程序员文章站 2022-07-10 21:29:51
背景:线上报表导出 跑了没1分钟堆内存溢出,emmm需求分析1、数据存储在es,都是单表 涉及表a,b,c,d,e,f 6张表2、每张表数据在百万以上,每个表取几个字段 拼接成一张大报表3、6张表之间对应关系,可能 1条a数据 几千条b数据痛点1、springboot 服务内存 256, new 对象太快 太多 没有足够的空间挪动回收解决:1、利用线程池+redis 异步执行、poolTaskExecutor.execute(new Runnable() {@Overri...

背景:线上报表导出 跑了没1分钟堆内存溢出,emmm
需求分析
1、数据存储在es,都是单表 涉及表a,b,c,d,e,f 6张表
2、每张表数据在百万以上,每个表取几个字段 拼接成一张大报表
3、6张表之间对应关系,可能 1条a数据 几千条b数据

痛点
1、springboot 服务内存 256, new 对象太快 太多 没有足够的空间挪动回收
记一次线上报表导出 OOM_Java head space
解决:
1、利用线程池+redis 异步执行、

	poolTaskExecutor.execute(new Runnable() {
			@Override
			public void run() {
				try{
					//redis 加锁
					redisCommonService.set(redisKey,CommonConst.IS_NO);
					
					log.info("子线程执行完毕。。。");
				}catch (Exception e){
					log.error("生成报表数据异常",e);
				}finally {
					//释放锁
					redisCommonService.set(redisKey,CommonConst.IS_YES,6000L);
				}

			}
		});

分析:一个子线程慢慢跑,跑一天都行 redis保证一个用户导出只能跑一个线程,避免子线程太多 导致oom

2、及时释放内存,使用 clear 和弱引用

	public static void batchSave(List<Map<String, Object>> list,Class c){
        
        log.info("总共 需要保存数据有:"+list.size());
        //分次批量插入数据,如果不分次,则可能数据太多导致插入不成功
        int allSize = list.size();  //总记录数
        int perSize = 500;  //每次插入的记录数
        int len = 0;  //插入的次数
        if (allSize % perSize == 0) {  //如果
            // 没有余数
            len = allSize / perSize;
        } else {
            len = allSize / perSize + 1;
        }

        List<Map<String, Object>> tempList = null;
        for (int i = 0; i < len; i++) {
            int startNum = i * perSize;
            int endNum = startNum + perSize;
            tempList = new ArrayList<>();
            WeakReference<Object> obj=new WeakReference<>(tempList);
            for (int index = startNum; index < endNum && index < allSize; index++) {
                tempList.add(list.get(index));
            }
            log.info("本次 是第"+i+"页,批量保存数据有:" + tempList.size());
			
            boolean saveDataBatch = coreService.saveDataBatch(tempList);
            log.info("批量保存返回结果:" + saveDataBatch);
            tempList.clear();
        }
        tempList=null;//htlp gc
    }

分析
1、tempList.clear(); 集合循环利用,避免new 太多
2、WeakReference 加快回收
记一次线上报表导出 OOM_Java head space
弱引用测试demo

	private static void test3(){
        Object obj1=new Object();
        WeakReference<Object> obj=new WeakReference<>(obj1);
        obj1=null; //help gc
        //内存够用,手动 gc
        System.gc();
        //内存不够用,自动 gc
        System.out.println(obj1);
        System.out.println(obj.get());
    }

3、由于 数据量大,每个操作分页查 分页存,一批500、存入单表即可

通过以上措施,成功避免了oom、生成30万报表大概 30分钟。___…

本文地址:https://blog.csdn.net/e_shi_yi_p/article/details/107333250