大家好,我是 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缓存行数,避免频繁刷新和内存溢出。

Java EasyExcel 导出报内存溢出如何解决的更多相关文章

  1. android通过BitmapFactory.decodeFile获取图片bitmap报内存溢出的解决办法

    android通过BitmapFactory.decodeFile获取图片bitmap报内存溢出的解决办法 原方法: public static Bitmap getSmallBitmap(Strin ...

  2. Java常见的几种内存溢出及解决方法

    Java常见的几种内存溢出及解决方法[情况一]:java.lang.OutOfMemoryError:Javaheapspace:这种是java堆内存不够,一个原因是真不够(如递归的层数太多等),另一 ...

  3. java内存溢出的解决思路

    原文地址:https://www.cnblogs.com/200911/p/3965108.html 内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能 ...

  4. spring boot启动报内存溢出的问题

    问题: springBoot项目,已经两次了,启动报内存溢出,内存泄露 分析: 内存泄露是因为垃圾回收器想要回收程序不用的对象,但是该对象还有引用存在 解决: 1.第一次是mybatis文件和Java ...

  5. .NET客户端下载SQL Server数据库中文件流保存的大电子文件方法(不会报内存溢出异常)

    .NET客户端下载SQL Server数据库中文件流保存的大电子文件方法(不会报内存溢出异常) 前段时间项目使用一次性读去SQL Server中保存的电子文件的文件流然后返回给客户端保存下载电子文件, ...

  6. java中三种常见内存溢出错误的处理方法

    更多 10   相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深入的 ...

  7. java中三种常见内存溢出错误的处理方法(good)

    相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深入的认识. 在解决j ...

  8. OutOfMemoryError(内存溢出)解决办法

    第一种OutOfMemoryError: PermGen space 发生这种问题的原意是程序中使用了大量的jar或class,使java虚拟机装载类的空间不够,与Permanent Generati ...

  9. Jmeter报内存溢出解决方案

    描述:wimdows环境,做上传图片接口测试,涉及图片合成和上传,图片采用base64编码.每1s启动200线程的时候,Jmeter报内存溢出错误. 解决方案: 1.修改jmeter.bat: set ...

  10. zabbix3.0.4关于java服务端程序内存溢出的处理

    关于java服务端程序内存溢出的处理 java服务端程序内存溢出会产生jvm.log文件,此时程序会挂掉,无法正常处理业务,需要重启服务 思路: 当存在jvm.log这个文件的时候则触发clean_j ...

随机推荐

  1. Python 环境傻瓜式搭建 :Anaconda概述

    Anaconda概述 Anaconda是一个用于科学计算的Python发行版,支持 Linux, Mac, Windows系统,提供了包管理与环境管理的功能,可以很方便地解决多版本python并存.切 ...

  2. 14-canvas绘制柱状图

    1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...

  3. PowerShell快速修改多个文件的名称

      本文介绍基于PowerShell语言,对文件夹中全部文件的名称加以批量替换.修改的方法.   在之前的文章中,我们介绍了基于Python语言,批量修改大量文件的名称的方法.当时我们修改文件名的需求 ...

  4. mybatis打印sql 转

    我们在使用mybatis开发过程中,经常需要打印sql以及输入输出,下面说一下mybatis结合log4j打印sql的. 1.添加mybatis配置 mybatis的日志打印方式比较多,SLF4J | ...

  5. Linux库概念,动态库和静态库的制作,如何移植第三方库

    一.什么是库? 在windows平台和linux平台下都大量存在着库.一般是软件作者为了发布方便.替换方便或二次开发目的,而发布的一组可以单独与应用程序进行compile time或runtime链接 ...

  6. JavaScript设计模式样例十六 —— 备忘录模式

    备忘录模式(Memento Pattern) 定义:保存一个对象的某个状态,以便在适当的时候恢复对象.目的:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.场景:数据缓存. ...

  7. Qt 设置快捷键

    Qt设置快捷键 本文仅供本人知识总结使用,所以内容会比较浅显,不喜勿喷. 文章目录 Qt设置快捷键 一.需要的类 QShortcut 函数: 二.设置快捷键 官方文档原文翻译: 我的理解: 一.需要的 ...

  8. Kafka Topic 中明明有可拉取的消息,为什么 poll 不到

    开心一刻 今天小学女同学给我发消息她:你现在是毕业了吗我:嗯,今年刚毕业她给我发了一张照片,怀里抱着一只大橘猫她:我的眯眯长这么大了,好看吗我:你把猫挪开点,它挡住了,我看不到她:你是 sb 吗,滚我 ...

  9. POA:已开源,蚂蚁集团提出同时预训练多种尺寸网络的自监督范式 | ECCV 2024

    论文提出一种新颖的POA自监督学习范式,通过弹性分支设计允许同时对多种尺寸的模型进行预训练.POA可以直接从预训练teacher生成不同尺寸的模型,并且这些模型可以直接用于下游任务而无需额外的预训练. ...

  10. C语言:应用程序增加库函数rand的步骤

    rand函数用来生成随机数,函数原型为int rand( void ); 返回值为生成的随机数,范围0~32767.在调用rand之前可以用srand函数初始化随机数发生器来生成更随机的数. 可以通过 ...