POI导出百万条数据至Excel 设-Xmx1024m仍报Java heap space错误
问题根因
你遇到的OOM是典型的POI原生XSSF模式处理大Excel内存溢出问题,和JVM堆内存调的大小没有本质关系:
- 原生
XSSFWorkbook读写xlsx的实现逻辑是把整个文件的所有结构(行、单元格、样式、共享字符串表)全量加载到堆内存中维护,xlsx本质是XML压缩包,内存对象的膨胀比极高,通常原始数据和内存占用比能到1:10甚至更高。 - 从你的错误栈可以直接定位到OOM触发点:
SharedStringsTable.addEntry,XSSF默认会把所有字符串类型的单元格值统一存在共享字符串表中,每写入一个字符串就要在内存中维护对应的XML节点结构,你当前数据单条字段的文本内容较长,仅写入6000余条时,堆内存占用就已经突破1G上限。哪怕把-Xmx调到10G以上,100万条数据的全量内存结构也会把堆撑爆,完全不具备可行性。 - 你的代码里还有两个隐性bug:一是
createRow和createCell都强转了short类型,short最大值仅为32767,就算解决内存问题,写到第32768行时就会抛出参数非法异常;二是循环内两次调用rs.getObject(i+1).toString(),重复读取结果集做了无用功,且没有做null判断,字段为空时会直接抛空指针。
可行解决方案
按改造成本和实用性从高到低排序:
- 优先方案:改用SXSSF流式API导出
SXSSF是POI官方提供的专门处理大xlsx导出的流式实现,核心逻辑是在内存中维护一个固定大小的行滑动窗口,超过窗口阈值的行会被自动序列化为XML片段写入磁盘临时文件,不会长期占用堆内存,百万级数据导出仅需数百MB堆内存即可稳定运行,改造成本极低。
核心改造代码示例:
// 初始化SXSSF工作簿,指定内存窗口保留1000行,超出部分自动刷入磁盘临时文件 SXSSFWorkbook wb = new SXSSFWorkbook(1000); Sheet sheet = wb.createSheet("导出数据"); // 全局仅创建一次样式,不要在循环内新建样式 CellStyle cellStyle2 = wb.createCellStyle(); // 省略表头创建逻辑 int index = 1; // 建议给JDBC语句设置fetchSize,避免DB2驱动把全量结果集拉到客户端内存 statement.setFetchSize(1000); ResultSet rs = statement.executeQuery(); while (rs.next()) { // 去掉short强转,SXSSF支持int类型行号 Row row = sheet.createRow(index); for (int i = 0; i < columns; i++) { Object cellVal = rs.getObject(i + 1); Cell cell = row.createCell(i); // 增加null判断避免空指针 if (cellVal != null) { cell.setCellValue(cellVal.toString()); } cell.setCellStyle(cellStyle2); } index++; // 单sheet最大支持1048576行,100万条数据刚好达上限,超过需要手动分sheet } // 写入输出流 try (FileOutputStream fos = new FileOutputStream("导出结果.xlsx")) { wb.write(fos); } // 必须调用dispose方法删除磁盘临时文件,避免产生垃圾文件 wb.dispose();
- 纯数据导出场景:直接导出CSV格式
如果不需要保留Excel样式、多sheet、公式等高级特性,直接生成CSV格式文本文件是性能最高的方案:逐行通过文件输出流写入文本,几乎不占用堆内存,100万条数据数秒即可导出完成,可直接用Excel打开编辑。 - 不推荐方案:无限制调大JVM堆内存
XSSF模式下内存膨胀比极高,100万条数据通常需要10G以上堆内存才有可能跑通,且GC压力极大,运行稳定性差,生产环境完全不建议使用。
内容的提问来源于stack exchange,提问作者matondo_gc
相关产品推荐
相关产品推荐

