EasyExcel为啥内存占用小?

典型回答 EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。他能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。 相比于POI,它有效的解决了内存溢出等问题(处理 100 万行 Excel 时,EasyExcel 内存在 50MB -100M左右,而 POI 可能需 1.5G - 2GB左右),那么他为什么占用内存更小呢?做了哪些特别的设计呢? EasyExcel 之所以能在处理大型 Excel 文件时保持较低的内存占用,主要归功于其SAX解析设计(逐行读取+事件机制)和磁盘缓存策略。(本文主要介绍EasyExcel的文件读取占用内存小的原因,文件写入其实和POI的 SXSSFWorkbook 原理一样的。直接看https://www.yuque.com/hollis666/ec96i7/ivczis4gyskog9q2 就可以了) PS:本文是基于https://github.com/alibaba/easyexcel/tree/master 的代码解读的,这个项目的作者后来又出了一个fastexcel,我大致看了下,相关代码差别不大。 SAX解析(核心) 首先,我们看一下EasyExcel官网上是有这么一段描述的: Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。 easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便 那么这个可以解决内存溢出问题的SAX是啥呢? SAX(Simple API for XML)是一种基于事件驱动的XML解析模型,与DOM(Document Object Model) 这种将整个 XML 加载进内存构建树状结构完全不同。它的核心设计理念是**逐行**边读边处理,不保留整个文档结构,是一种流式处理方式。 ...

March 22, 2026 · 2 min · santu

POI的如何做大文件的写入

典型回答 ✅什么是POI,为什么它会导致内存溢出? 上一篇中介绍了POI的内存溢出以及几种Workbook,那么,我们在做文件写入的时候,该如何选择呢?他们的在内存使用上有啥差异呢? 我们接下来分别使用XSSFWorkbook和SXSSFWorkbook来写入一个Excel文件,分别看一下堆内存的使用情况。 使用XSSF写入文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package excel.write; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.FileOutputStream; import java.io.IOException; import java.util.UUID; public class XSSFExcelTest { public static void main(String[] args) throws InterruptedException { // 创建一个新的工作簿 XSSFWorkbook workbook = new XSSFWorkbook(); // 创建一个新的表格 Sheet sheet = workbook.createSheet("Example Sheet"); for (int i = 0; i < 10000; i++) { // 创建行(从0开始计数) Row row = sheet.createRow(i); for (int j = 0; j < 100; j++) { // 在行中创建单元格(从0开始计数) Cell cell = row.createCell(j); // 设置单元格的值 cell.setCellValue(UUID.randomUUID().toString()); } } // 设置文件路径和名称 String filename = "example.xlsx"; try (FileOutputStream outputStream = new FileOutputStream(filename)) { // 将工作簿写入文件 workbook.write(outputStream); } catch (IOException e) { e.printStackTrace(); } finally { try { // 关闭工作簿资源 workbook.close(); } catch (IOException e) { e.printStackTrace(); } } } } � ...

March 22, 2026 · 3 min · santu

为啥POI的SXSSFWorkbook占用内存更小_

典型回答 SXSSFWorkbook 类是为了处理大型 Excel 文件而设计的。它的实现原理是通过将部分数据写入磁盘上的临时文件来减少内存占用。 在SXSSFWorkbook类中,有一个类叫做SheetDataWriter,这个类的作用就是将部分数据写入磁盘上的临时文件的。 1 2 3 4 5 6 7 8 9 public class SXSSFWorkbook implements Workbook { protected SheetDataWriter createSheetDataWriter() throws IOException { if(_compressTmpFiles) { return new GZIPSheetDataWriter(_sharedStringSource); } return new SheetDataWriter(_sharedStringSource); } } 写入过程是在 SheetDataWriter 的 writeRow 方法中实现的。此方法会被 SXSSFSheet 调用,以将行数据转换成 XML 并写入临时文件。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public void writeRow(int rownum, SXSSFRow row) throws IOException { if (_numberOfFlushedRows == 0) _lowestIndexOfFlushedRows = rownum; _numberLastFlushedRow = Math.max(rownum, _numberLastFlushedRow); _numberOfCellsOfLastFlushedRow = row.getLastCellNum(); _numberOfFlushedRows++; beginRow(rownum, row); Iterator<Cell> cells = row.allCellsIterator(); int columnIndex = 0; while (cells.hasNext()) { writeCell(columnIndex++, cells.next()); } endRow(); } writeRow方法会循环调用writeCell方法: ...

March 22, 2026 · 4 min · santu

什么是POI,为什么它会导致内存溢出?

典型回答 Apache POI,是一个非常流行的文档处理工具,通常大家会选择用它来处理Excel文件。但是在实际使用的时候,经常会遇到内存溢出的情况,那么,为啥他会导致内存溢出呢? Excel并没看到的那么小 我们通常见到的xlsx文件,其实是一个个压缩文件。它们把若干个XML格式的纯文本文件压缩在一起,Excel就是读取这些压缩文件的信息,最后展现出一个完全图形化的电子表格。 所以,如果我们把xlsx文件的后缀更改为.zip或.rar,再进行解压缩,就能提取出构成Excel的核心源码文件。解压后会发现解压后的文件中有3个文件夹和1个XML格式文件: _rels 文件夹 看里面数据像是一些基础的配置信息,比如 workbook 文件的位置等信息,一般不会去动它. docProps 文件夹 docProps 文件夹下重要的文件是一个 app.xml,这里面主要存放了 sheet 的信息,如果想添加或编辑 sheet 需要改这个文件.其他文件都是一些基础信息的数据,比如文件所有者,创建时间等. xl 文件夹 xl 文件夹是最重要的一个文件夹,里面存放了 Sheet 中的数据,行和列的格式,单元格的格式,sheet 的配置信息等等信息. 所以,实际上我们处理的xlsx文件实际上是一个经过高度压缩的文件格式,背后是有好多文件支持的。所以,我们看到的一个文件可能只有2M,但是实际上这个文件未压缩情况下可能要比这大得多。 也就是说,POI在处理的时候,处理的实际上并不只是我们看到的文件大小,实际上他的大小大好几倍。 这是为什么明明我们处理的文件只有100多兆,但是实际却可能占用1G内存的其中一个原因。当然这只是其中一个原因,还有一个原因,我们就需要深入到POI的源码中来看了。 POI的溢出原理 我们拿POI的文件读取来举例,一般来说文件读取出现内存溢出的情况更多一些。以下是一个POI文件导出的代码示例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class ExcelReadTest { public static void main(String[] args) { // 指定要读取的文件路径 String filename = "example.xlsx"; try (FileInputStream fileInputStream = new FileInputStream(new File(filename))) { // 创建工作簿对象 Workbook workbook = new XSSFWorkbook(fileInputStream); // 获取第一个工作表 Sheet sheet = workbook.getSheetAt(0); // 遍历所有行 for (Row row : sheet) { // 遍历所有单元格 for (Cell cell : row) { Thread.sleep(100); // 根据不同数据类型处理数据 switch (cell.getCellType()) { case STRING: System.out.print(cell.getStringCellValue() + "\t"); break; case NUMERIC: if (DateUtil.isCellDateFormatted(cell)) { System.out.print(cell.getDateCellValue() + "\t"); } else { System.out.print(cell.getNumericCellValue() + "\t"); } break; case BOOLEAN: System.out.print(cell.getBooleanCellValue() + "\t"); break; case FORMULA: System.out.print(cell.getCellFormula() + "\t"); break; default: System.out.print(" "); } } System.out.println(); } } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { throw new RuntimeException(e); } } } 这里面用到了一个关键的XSSFWorkbook类, ...

March 22, 2026 · 3 min · santu

如何针对大Excel做文件读取?

典型回答 在POI中,提供了SXSSFWorkbook,通过将部分数据写入磁盘上的临时文件来减少内存占用。但是SXSSFWorkbook只能用于文件写入,但是文件读取还是不行的,就像我们前面分析过的,Excel的文件读取还是会存在内存溢出的问题的。 ✅什么是POI,为什么它会导致内存溢出? ✅POI的如何做大文件的写入 那如果要解决这个问题,可以考虑使用EasyExcel EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。他能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。 关于使用XSSFWorkbook和EasyExcel的文件读取,我这里也做了个内存占用的对比: XSSFWorkbook文件读取 读取一个27.3 MB的文件(文件的生成代码在✅POI的如何做大文件的写入中) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package excel.read; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class XSSFExcelReadTest { public static void main(String[] args) { // 指定要读取的文件路径 String filename = "example.xlsx"; try (FileInputStream fileInputStream = new FileInputStream(new File(filename))) { // 创建工作簿对象 Workbook workbook = new XSSFWorkbook(fileInputStream); // 获取第一个工作表 Sheet sheet = workbook.getSheetAt(0); // 遍历所有行 for (Row row : sheet) { // 遍历所有单元格 for (Cell cell : row) { // 根据不同数据类型处理数据 switch (cell.getCellType()) { case STRING: System.out.print(cell.getStringCellValue() + "\t"); break; case NUMERIC: if (DateUtil.isCellDateFormatted(cell)) { System.out.print(cell.getDateCellValue() + "\t"); } else { System.out.print(cell.getNumericCellValue() + "\t"); } break; case BOOLEAN: System.out.print(cell.getBooleanCellValue() + "\t"); break; case FORMULA: System.out.print(cell.getCellFormula() + "\t"); break; default: System.out.print(" "); } } System.out.println(); } } catch (IOException e) { e.printStackTrace(); } } } 同样使用Arthas查看内存占用情况: ...

March 22, 2026 · 2 min · santu

留言给博主