码农终将翻身 2024-12-10 17:40 采纳率: 75%
浏览 19

java easyexcel导出excel文件oom

img

java easyexcel导出excel文件oom,但是easyexcel底层用不到这两个吧,哪里占用的呢


    public void exportDetailInfoList(@RequestBody Map params,HttpServletResponse response,UserInfo userInfo) throws IOException {
        log.info("-----------缴税详细信息导出: " + JsonUtils.beanToJson(params));
        try {
            Map<String, Object> oldParams = new HashMap<>(params);
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setCharacterEncoding("utf-8");
//             这里URLEncoder.encode可以防止中文乱码
            String excelName = URLEncoder.encode("缴税详细信息", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + excelName + ".xlsx");
            long start = System.currentTimeMillis();
            InputStream templateInputStream = this.getClass().getClassLoader().getResourceAsStream("template/详细信息查询导出模板,表头格式.xlsx");
            //设置输出流和模板信息
            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).useDefaultStyle(false).withTemplate(templateInputStream).build();
            WriteSheet writeSheet = EasyExcel.writerSheet().build();
            //开启自动换行,自动换行表示每次写入一条list数据是都会重新生成一行空行,此选项默认是关闭的,需要提前设置为true
            FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
            Map<String, Object> query = new HashMap<>();
            params.put("offset",1);
            params.put("limit",1);//只为获取总条数
            query = taxService.queryDetailInfoList(params,userInfo.getUser());
            Integer totalNum = Integer.parseInt(query.get("total").toString()) ;
            List<TaxDetailInfoExcel> firstData = JsonUtils.beanToArray(query.get("rows"), TaxDetailInfoExcel.class);
            int total = totalNum.intValue();
            int limit = 10000;
            int times = (total + limit - 1)/limit;

            Integer num = 1;
            for (int i = 1; i <= times; i++) {
                params.putAll(oldParams);
                params.put("offset",i);
                params.put("limit",limit);
                params.put("notCount","1");
                //todo 不要每次都查总
                query = taxService.queryDetailInfoList(params,userInfo.getUser());
                List<TaxDetailInfoExcel> subList = JsonUtils.beanToArray(query.get("rows"), TaxDetailInfoExcel.class);
                for (TaxDetailInfoExcel taxDetailInfoExcel : subList) {
                    taxDetailInfoExcel.setNum((num++).toString());
                }
                long list1 = System.currentTimeMillis();
                FillWrapper fillWrapper1 = new FillWrapper("detail",subList);
                // 打印日志以确认进度
                log.info("Processing batch: " + i);
                excelWriter.fill(fillWrapper1,fillConfig,writeSheet);
                subList.clear();  // 清除内存中的数据,避免内存膨胀
                long list2 = System.currentTimeMillis();
                log.info("list2-list1:" + (list2 - list1) + "ms");
            }

            params.putAll(oldParams);
            params.remove("notCount");
            Map<String, Object> totalMap = taxService.queryDetailInfoTotal(params, userInfo.getUser());
            List<TaxDetailInfoExcel> totalData = JsonUtils.beanToArray(totalMap.get("rows"), TaxDetailInfoExcel.class);
            FillWrapper fillWrapper2 = new FillWrapper("total",totalData);
            excelWriter.fill(fillWrapper2,writeSheet);
            //map的key就是excel的{key}
            String createTime =  (String) oldParams.get("between_b_printDate");
            String tradeTaxNo = "";

            if(!com.easipass.utils.StringUtils.isEmpty(params.get("like_a_tradeTaxNo")) && CollectionUtils.isNotEmpty(firstData)){
                TaxDetailInfoExcel taxDetailInfoExcel = firstData.get(0);
                String like_a_tradeTaxNo = taxDetailInfoExcel.getTradeTaxNo();
                if (like_a_tradeTaxNo != null && like_a_tradeTaxNo.length() > 0) {
                    // 如果长度大于5,截取0-5个字符
                    if (like_a_tradeTaxNo.length() > 5) {
                        like_a_tradeTaxNo = like_a_tradeTaxNo.substring(0, 5);
                    }
                    // 如果长度小于5,截取所有字符
                    else {
                        like_a_tradeTaxNo = like_a_tradeTaxNo.substring(0);
                    }
                }
                tradeTaxNo = like_a_tradeTaxNo;
            }
            String[] split = createTime.split(",");
            Map<String, Object> map = new HashMap<>();
            map.put("startDate",split[0]);
            map.put("endDate",split[1]);
            map.put("tradeTaxNo",tradeTaxNo);
            map.put("fillDate", DateUtils.getDateString("yyyy年MM月"));
            excelWriter.fill(map,writeSheet);
            excelWriter.finish();
            long end = System.currentTimeMillis();
            log.info("导出缴税详细信息耗时:" + (end - start) + "ms");
        } catch (Exception e) {
            log.info("缴税详细信息导出失败 ");
            log.info(e.getMessage(), e);
            throw e;
        }
    }
  • 写回答

2条回答 默认 最新

  • 越重天 新星创作者: Java技术领域 2024-12-13 12:39
    关注

    本答案参考多次询问ChatGPT-3.5后选择的最佳回答,若是满意,还请采纳,谢谢。

    1. 可能导致OOM(内存溢出)的原因分析
      • 数据量过大
        • 虽然在代码中对数据进行了分批处理(每次处理limit条数据),但是如果total非常大,例如百万级甚至更多的数据量,即使分批处理,在Java堆内存有限的情况下,仍然可能导致内存溢出。特别是如果堆内存设置较小,而数据处理过程中产生的临时对象(如TaxDetailInfoExcel对象)占用的内存不断累积,就可能超出内存限制。
        • 例如,如果TaxDetailInfoExcel对象包含很多字段,每个对象占用较大内存空间,当处理大量数据时,内存消耗会迅速增加。假设TaxDetailInfoExcel对象每个占用1KB内存,每次处理10000条数据,仅仅这一批次的数据在内存中的占用就达到10MB,如果有很多批次,很容易超出内存限制。
      • 对象引用未释放
        • 在代码中虽然有subList.clear();语句来清除subList中的数据,但是需要注意的是,这只是清空了List中的元素引用,如果TaxDetailInfoExcel对象内部还持有其他对象的引用,这些被引用的对象可能仍然无法被垃圾回收。
        • 例如,如果TaxDetailInfoExcel对象中有一个BigDecimal类型的字段,并且这个字段在其他地方被缓存或者持有了一些大对象的引用,仅仅清空subList是不够的,这些对象仍然可能占用大量内存。
      • EasyExcel本身的缓存或资源占用
        • 虽然你提到EasyExcel底层可能没有明显涉及到导致OOM的操作,但EasyExcel在处理Excel文件时可能会有一些内部的缓存机制。例如,在构建ExcelWriter时,可能会在内存中缓存一些样式信息、格式信息等。如果处理的数据量非常大,这些缓存信息可能会不断增长,导致内存溢出。
    2. 解决方案建议
      • 优化内存使用
        • 对于TaxDetailInfoExcel对象,可以检查其内部结构,尽量减少不必要的字段或者对象引用。如果某些字段在导出过程中不需要,可以不进行填充或者在对象构建时不初始化这些字段。
        • 例如,如果TaxDetailInfoExcel对象中有一个BigDecimal类型的字段用于计算,但在导出时不需要,就可以在构建用于导出的对象时不初始化这个字段,从而减少每个对象的内存占用。
      • 调整JVM内存参数
        • 根据实际数据量的大小,可以适当增加JVM的堆内存。例如,可以通过 -Xmx参数来增加最大堆内存。如果服务器内存充足,可以将 -Xmx设置为一个较大的值,如 -Xmx2g(表示最大堆内存为2GB)。但是需要注意,这只是一种临时解决方案,不能从根本上解决内存泄漏或者不合理的内存使用问题。
      • 检查EasyExcel版本和配置
        • 确保使用的EasyExcel版本是最新的,因为新版本可能会修复一些内存相关的问题或者优化内存使用。同时,可以检查EasyExcel的配置参数,看是否有一些不必要的配置导致了内存占用过大。例如,某些高级的样式配置可能会占用较多内存,如果不是必须的,可以简化这些配置。
    评论

报告相同问题?

问题事件

  • 修改了问题 12月10日
  • 创建了问题 12月10日