Java EasyExcel导出报内存溢出的解决办法

 更新时间:2024年10月28日 12:06:26   作者:威哥爱编程  
使用EasyExcel进行大数据量导出时容易导致内存溢出,特别是在导出百万级别的数据时,你有遇到过这种情况吗,以下是小编整理的解决该问题的一些常见方法,需要的朋友可以参考下

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;
        }
    }
}

来解释一下代码

  • SXSSFWorkbookSXSSFWorkbook(100)表示内存中最多保留100行数据,超过的部分会写入临时文件,节省内存。
  • 批次处理:通过batchSize控制每批次写入的数据量,以减少内存消耗。totalRows设置为1,000,000表示导出100万条数据。
  • 模拟数据生成getDataBatch方法模拟分页获取数据,每次返回一批数据。
  • 清除缓存行:每次写入一批数据后,通过flushRows(batchSize)将缓存的行从内存中清除,以控制内存占用。
  • 压缩临时文件workbook.setCompressTempFiles(true)启用临时文件压缩,进一步减少磁盘空间占用。

需要注意的事项

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

以上就是Java EasyExcel导出报内存溢出的解决办法的详细内容,更多关于Java EasyExcel导出内存溢出的资料请关注脚本之家其它相关文章!

相关文章

  • Java实现自动生成缩略图片

    Java实现自动生成缩略图片

    这篇文章主要为大家详细介绍了Java实现自动生成缩略图片,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • 简单了解java集合框架LinkedList使用方法

    简单了解java集合框架LinkedList使用方法

    这篇文章主要介绍了简单了解java集合框架LinkedList使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • SpringBoot实现文件下载功能的方式分享

    SpringBoot实现文件下载功能的方式分享

    这篇文章主要为大家详细介绍了SpringBoot这哪个实现文件下载功能的几种方式,文中的实现方法简介易懂,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-03-03
  • 解决SpringBoot项目使用多线程处理任务时无法通过@Autowired注入bean问题

    解决SpringBoot项目使用多线程处理任务时无法通过@Autowired注入bean问题

    这篇文章主要介绍了SpringBoot项目使用多线程处理任务时无法通过@Autowired注入bean问题的解决方法,需要的朋友可以参考下
    2018-09-09
  • Jackson中json格式的字符串与对象的互相转换方式

    Jackson中json格式的字符串与对象的互相转换方式

    这篇文章主要介绍了Jackson中json格式的字符串与对象的互相转换方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • IntelliJ IDEA设置JVM运行参数的操作方法

    IntelliJ IDEA设置JVM运行参数的操作方法

    这篇文章主要介绍了IntelliJ IDEA设置JVM运行参数的操作方法,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2018-03-03
  • Springboot打包部署修改配置文件的方法

    Springboot打包部署修改配置文件的方法

    这篇文章主要介绍了Springboot打包部署修改配置文件的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • maven多个仓库查询的优先级顺序案例讲解

    maven多个仓库查询的优先级顺序案例讲解

    这篇文章主要介绍了maven多个仓库查询的优先级顺序,考虑到我们常用的配置文件是conf/settings.xml和工程里面的pom.xml文件,我们针对这两个文件的结合来分析仓库的使用顺序,需要的朋友可以参考下
    2023-04-04
  • SpringBoot+随机盐值+双重MD5实现加密登录

    SpringBoot+随机盐值+双重MD5实现加密登录

    数据加密在很多项目上都可以用到,大部分都会采用MD5进行加密,本文主要介绍了SpringBoot+随机盐值+双重MD5实现加密登录,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • 深入解读分布式事务Seata的XA模式

    深入解读分布式事务Seata的XA模式

    这篇文章主要介绍了深入解读分布式事务Seata的XA模式,XA 规范 描述了全局的事务管理器与局部的资源管理器之间的接口,XA规范 的目的是允许的多个资源在同一事务中访问,这样可以使 ACID 属性跨越应用程序而保持有效,需要的朋友可以参考下
    2023-08-08

最新评论