写点什么

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

作者:威哥爱编程
  • 2024-10-28
    北京
  • 本文字数:3307 字

    阅读完需:约 11 分钟

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

EasyExcel 大数据量导出常见方法

1. 分批写入

  • EasyExcel 支持分批写入数据,可以将数据分批加载到内存中,分批写入 Excel 文件,避免一次性将大量数据加载到内存中。

  • 示例代码


     String fileName = "large_data.xlsx";     ExcelWriter excelWriter = EasyExcel.write(fileName).build();     WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").build();
// 假设每次写入10000条数据 int batchSize = 10000; List<Data> dataList; int pageIndex = 0; do { // 分页获取数据 dataList = getDataByPage(pageIndex++, batchSize); excelWriter.write(dataList, writeSheet); } while (dataList.size() == batchSize);
// 关闭资源 excelWriter.finish();
复制代码

2. 设置合适的 JVM 内存

  • 针对大数据导出场景,可以尝试增大 JVM 的内存分配,例如:


     java -Xms512M -Xmx4G -jar yourApp.jar
复制代码


  • 解释

  • -Xms512M:设置初始堆大小为 512MB。

  • -Xmx4G:设置最大堆大小为 4GB。

3. 减少数据对象的复杂性

  • 导出数据时,尽量简化数据对象,避免不必要的嵌套和多余字段的加载,以减少对象占用的内存空间。

4. 关闭自动列宽设置

  • EasyExcel 的自动列宽功能会占用大量内存,特别是在数据量较大的情况下。关闭自动列宽可以节省内存。

  • 示例代码


     EasyExcel.write(fileName)             .registerWriteHandler(new SimpleWriteHandler()) // 不使用自动列宽             .sheet("Sheet1")             .doWrite(dataList);
复制代码

5. 使用 Stream 导出(适合大数据)

  • 利用OutputStream分批写入数据,减少内存消耗。通过BufferedOutputStream可以进一步提高性能。

  • 示例代码


     try (OutputStream out = new BufferedOutputStream(new FileOutputStream(fileName))) {         ExcelWriter excelWriter = EasyExcel.write(out).build();         WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").build();         int pageIndex = 0;         List<Data> dataList;         do {             dataList = getDataByPage(pageIndex++, batchSize);             excelWriter.write(dataList, writeSheet);         } while (dataList.size() == batchSize);         excelWriter.finish();     } catch (IOException e) {         e.printStackTrace();     }
复制代码

6. 选择合适的数据导出工具

  • 如果数据量非常大,可以考虑切换到支持更高性能的导出工具(如 Apache POI 的SXSSFWorkbook),适合导出百万级别数据量,但配置和使用会更复杂。


亮点来了,那要如何使用 POI 的 SXSSFWorkbook 来导出百万级别的数据量呢?

Apache POI 的 SXSSFWorkbook 实现百万级别数据量的导出案例

使用 Apache POI 的SXSSFWorkbook可以处理大数据量的 Excel 导出,因为SXSSFWorkbook基于流式写入,不会将所有数据加载到内存中,而是使用临时文件进行缓存,这样可以显著减少内存消耗,适合百万级别数据的导出。下面我们来看一个完整的实现示例。

代码如下

import org.apache.poi.ss.usermodel.*;import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import java.io.FileOutputStream;import java.io.IOException;import java.util.ArrayList;import java.util.List;
public class LargeDataExportExample {
public static void main(String[] args) { // 文件输出路径 String filePath = "vg_large_data_export.xlsx"; // 导出百万级数据 exportLargeData(filePath); }
private static void exportLargeData(String filePath) { // 每次写入的批次大小 final int batchSize = 10000; // 数据总条数 final int totalRows = 1_000_000;
// 创建SXSSFWorkbook对象,内存中只保留100行,超过的部分会写入临时文件 SXSSFWorkbook workbook = new SXSSFWorkbook(100); workbook.setCompressTempFiles(true); // 启用临时文件压缩
// 创建工作表 Sheet sheet = workbook.createSheet("Large Data");
// 创建标题行 Row headerRow = sheet.createRow(0); String[] headers = {"ID", "Name", "Age"}; for (int i = 0; i < headers.length; i++) { Cell cell = headerRow.createCell(i); cell.setCellValue(headers[i]); }
int rowNum = 1; // 数据开始的行号
try { // 按批次写入数据 for (int i = 0; i < totalRows / batchSize; i++) { // 模拟获取每批数据 List<Data> dataList = getDataBatch(rowNum, batchSize); // 将数据写入到Excel中 for (Data data : dataList) { Row row = sheet.createRow(rowNum++); row.createCell(0).setCellValue(data.getId()); row.createCell(1).setCellValue(data.getName()); row.createCell(2).setCellValue(data.getAge()); }
// 处理完成一批数据后,可以选择清除缓存数据,防止内存溢出 ((SXSSFSheet) sheet).flushRows(batchSize); // 清除已写的行缓存 }
// 将数据写入文件 try (FileOutputStream fos = new FileOutputStream(filePath)) { workbook.write(fos); } System.out.println("数据导出完成:" + filePath);
} catch (IOException e) { e.printStackTrace(); } finally { // 关闭workbook并删除临时文件 workbook.dispose(); } }
/** * 模拟分页获取数据 */ private static List<Data> getDataBatch(int startId, int batchSize) { List<Data> dataList = new ArrayList<>(batchSize); for (int i = 0; i < batchSize; i++) { dataList.add(new Data(startId + i, "Name" + (startId + i), 20 + (startId + i) % 50)); } return dataList; }
// 数据类 static class Data { private final int id; private final String name; private final int age;
public Data(int id, String name, int age) { this.id = id; this.name = name; this.age = age; }
public int getId() { return id; }
public String getName() { return name; }
public int getAge() { return age; } }}
复制代码

来解释一下代码

  1. SXSSFWorkbookSXSSFWorkbook(100)表示内存中最多保留 100 行数据,超过的部分会写入临时文件,节省内存。

  2. 批次处理:通过batchSize控制每批次写入的数据量,以减少内存消耗。totalRows设置为 1,000,000 表示导出 100 万条数据。

  3. 模拟数据生成getDataBatch方法模拟分页获取数据,每次返回一批数据。

  4. 清除缓存行:每次写入一批数据后,通过flushRows(batchSize)将缓存的行从内存中清除,以控制内存占用。

  5. 压缩临时文件workbook.setCompressTempFiles(true)启用临时文件压缩,进一步减少磁盘空间占用。


需要注意的事项


  • 临时文件:SXSSFWorkbook 会在系统临时文件夹中生成临时文件,需要确保磁盘空间足够。

  • 资源释放:完成数据写入后需要调用workbook.dispose()以清理临时文件。

  • 性能优化:可根据机器内存调整batchSizeSXSSFWorkbook缓存行数,避免频繁刷新和内存溢出。

发布于: 刚刚阅读数: 9
用户头像

华为 HDE、CSDN 博客专家、Java畅销书作者 2018-05-30 加入

全栈领域优质创作者(Java/HarmonyOS/AI),公众号:威哥爱编程

评论

发布
暂无评论
Java EasyExcel 导出报内存溢出如何解决_Java_威哥爱编程_InfoQ写作社区