Java EasyExcel 导出报内存溢出如何解决

打印 上一主题 下一主题

主题 909|帖子 909|积分 2727

各人好,我是 V 哥。使用EasyExcel举行大数据量导出时轻易导致内存溢出,特别是在导出百万级别的数据时。你有碰到过这种情况吗,以下是V 哥整理的解决该问题的一些常见方法,分享给各人,接待一起讨论:
EasyExcel大数据量导出常见方法

1. 分批写入


  • EasyExcel支持分批写入数据,可以将数据分批加载到内存中,分批写入Excel文件,避免一次性将大量数据加载到内存中。
  • 示例代码
  1.      String fileName = "large_data.xlsx";
  2.      ExcelWriter excelWriter = EasyExcel.write(fileName).build();
  3.      WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").build();
  4.      // 假设每次写入10000条数据
  5.      int batchSize = 10000;
  6.      List<Data> dataList;
  7.      int pageIndex = 0;
  8.      do {
  9.          // 分页获取数据
  10.          dataList = getDataByPage(pageIndex++, batchSize);
  11.          excelWriter.write(dataList, writeSheet);
  12.      } while (dataList.size() == batchSize);
  13.      // 关闭资源
  14.      excelWriter.finish();
复制代码
2. 设置合适的JVM内存


  • 针对大数据导出场景,可以实行增大JVM的内存分配,例如:
  1.      java -Xms512M -Xmx4G -jar yourApp.jar
复制代码

  • 解释

    • -Xms512M:设置初始堆大小为512MB。
    • -Xmx4G:设置最大堆大小为4GB。

3. 淘汰数据对象的复杂性


  • 导出数据时,尽量简化数据对象,避免不必要的嵌套和多余字段的加载,以淘汰对象占用的内存空间。
4. 关闭自动列宽设置


  • EasyExcel的自动列宽功能会占用大量内存,特别是在数据量较大的情况下。关闭自动列宽可以节省内存。
  • 示例代码
  1.      EasyExcel.write(fileName)
  2.              .registerWriteHandler(new SimpleWriteHandler()) // 不使用自动列宽
  3.              .sheet("Sheet1")
  4.              .doWrite(dataList);
复制代码
5. 使用Stream导出(适合大数据)


  • 利用OutputStream分批写入数据,淘汰内存斲丧。通过BufferedOutputStream可以进一步提高性能。
  • 示例代码
  1.      try (OutputStream out = new BufferedOutputStream(new FileOutputStream(fileName))) {
  2.          ExcelWriter excelWriter = EasyExcel.write(out).build();
  3.          WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").build();
  4.          int pageIndex = 0;
  5.          List<Data> dataList;
  6.          do {
  7.              dataList = getDataByPage(pageIndex++, batchSize);
  8.              excelWriter.write(dataList, writeSheet);
  9.          } while (dataList.size() == batchSize);
  10.          excelWriter.finish();
  11.      } catch (IOException e) {
  12.          e.printStackTrace();
  13.      }
复制代码
6. 选择合适的数据导出工具


  • 假如数据量非常大,可以考虑切换到支持更高性能的导出工具(如Apache POI的SXSSFWorkbook),适合导出百万级别数据量,但配置和使用会更复杂。
亮点来了,那要如何使用 POI 的 SXSSFWorkbook来导出百万级别的数据量呢?
Apache POI的SXSSFWorkbook 实现百万级别数据量的导出案例

使用Apache POI的SXSSFWorkbook可以处理大数据量的Excel导出,因为SXSSFWorkbook基于流式写入,不会将所有数据加载到内存中,而是使用暂时文件举行缓存,这样可以显著淘汰内存斲丧,适合百万级别数据的导出。下面我们来看一个完整的实现示例。
代码如下
  1. import org.apache.poi.ss.usermodel.*;
  2. import org.apache.poi.xssf.streaming.SXSSFWorkbook;
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. public class LargeDataExportExample {
  8.     public static void main(String[] args) {
  9.         // 文件输出路径
  10.         String filePath = "vg_large_data_export.xlsx";
  11.         
  12.         // 导出百万级数据
  13.         exportLargeData(filePath);
  14.     }
  15.     private static void exportLargeData(String filePath) {
  16.         // 每次写入的批次大小
  17.         final int batchSize = 10000;
  18.         // 数据总条数
  19.         final int totalRows = 1_000_000;
  20.         // 创建SXSSFWorkbook对象,内存中只保留100行,超过的部分会写入临时文件
  21.         SXSSFWorkbook workbook = new SXSSFWorkbook(100);
  22.         workbook.setCompressTempFiles(true); // 启用临时文件压缩
  23.         // 创建工作表
  24.         Sheet sheet = workbook.createSheet("Large Data");
  25.         // 创建标题行
  26.         Row headerRow = sheet.createRow(0);
  27.         String[] headers = {"ID", "Name", "Age"};
  28.         for (int i = 0; i < headers.length; i++) {
  29.             Cell cell = headerRow.createCell(i);
  30.             cell.setCellValue(headers[i]);
  31.         }
  32.         int rowNum = 1; // 数据开始的行号
  33.         try {
  34.             // 按批次写入数据
  35.             for (int i = 0; i < totalRows / batchSize; i++) {
  36.                 // 模拟获取每批数据
  37.                 List<Data> dataList = getDataBatch(rowNum, batchSize);
  38.                
  39.                 // 将数据写入到Excel中
  40.                 for (Data data : dataList) {
  41.                     Row row = sheet.createRow(rowNum++);
  42.                     row.createCell(0).setCellValue(data.getId());
  43.                     row.createCell(1).setCellValue(data.getName());
  44.                     row.createCell(2).setCellValue(data.getAge());
  45.                 }
  46.                 // 处理完成一批数据后,可以选择清除缓存数据,防止内存溢出
  47.                 ((SXSSFSheet) sheet).flushRows(batchSize); // 清除已写的行缓存
  48.             }
  49.             // 将数据写入文件
  50.             try (FileOutputStream fos = new FileOutputStream(filePath)) {
  51.                 workbook.write(fos);
  52.             }
  53.             System.out.println("数据导出完成:" + filePath);
  54.         } catch (IOException e) {
  55.             e.printStackTrace();
  56.         } finally {
  57.             // 关闭workbook并删除临时文件
  58.             workbook.dispose();
  59.         }
  60.     }
  61.     /**
  62.      * 模拟分页获取数据
  63.      */
  64.     private static List<Data> getDataBatch(int startId, int batchSize) {
  65.         List<Data> dataList = new ArrayList<>(batchSize);
  66.         for (int i = 0; i < batchSize; i++) {
  67.             dataList.add(new Data(startId + i, "Name" + (startId + i), 20 + (startId + i) % 50));
  68.         }
  69.         return dataList;
  70.     }
  71.     // 数据类
  72.     static class Data {
  73.         private final int id;
  74.         private final String name;
  75.         private final int age;
  76.         public Data(int id, String name, int age) {
  77.             this.id = id;
  78.             this.name = name;
  79.             this.age = age;
  80.         }
  81.         public int getId() {
  82.             return id;
  83.         }
  84.         public String getName() {
  85.             return name;
  86.         }
  87.         public int getAge() {
  88.             return age;
  89.         }
  90.     }
  91. }
复制代码
来解释一下代码


  • SXSSFWorkbook:SXSSFWorkbook(100)表现内存中最多保存100行数据,超过的部分会写入暂时文件,节省内存。
  • 批次处理:通过batchSize控制每批次写入的数据量,以淘汰内存斲丧。totalRows设置为1,000,000表现导出100万条数据。
  • 模仿数据天生:getDataBatch方法模仿分页获取数据,每次返回一批数据。
  • 清除缓存行:每次写入一批数据后,通过flushRows(batchSize)将缓存的行从内存中清除,以控制内存占用。
  • 压缩暂时文件:workbook.setCompressTempFiles(true)启用暂时文件压缩,进一步淘汰磁盘空间占用。
需要注意的事项

  • 暂时文件:SXSSFWorkbook会在体系暂时文件夹中天生暂时文件,需要确保磁盘空间充足。
  • 资源释放:完成数据写入后需要调用workbook.dispose()以清理暂时文件。
  • 性能优化:可根据机器内存调整batchSize和SXSSFWorkbook缓存行数,避免频繁刷新和内存溢出。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

去皮卡多

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表