核心是改用流式读取api(sax模式)替代全量加载,通过xssfreader+defaulthandler监听xml标签实现内存恒定占用,解析后立即处理并清空缓冲,同时规范sxssf写入清理、禁用dom式调用、加强资源监控与兜底防护。

核心是把“全量加载”换成“边读边扔”,避免对象长期驻留堆中。Apache POI 默认的 XSSFWorkbook 和 HSSFWorkbook 会把整个 Excel 结构(样式、公式、共享字符串、所有单元格)一次性构建为 Java 对象,内存占用随行数线性甚至指数增长,且因静态缓存、Finalizer 队列延迟释放等原因,close() 后仍有大量对象无法被 GC 回收——这就是泄漏的实质。
改用流式读取 API(SAX 模式)
这是最彻底的解决方式,绕过 DOM 树构建,只响应 XML 标签事件,内存占用恒定(O(1)):
- 使用
XSSFReader+XMLReader+ 自定义DefaultHandler,监听<row></row>、<c></c>、<v></v>等标签 - 解析完一行数据后立即交给业务逻辑处理,不保留 Row/Cell 对象引用
- 共享字符串表(
SharedStringsTable)需提前加载一次,但仅此一张表,不随行数增长 - 务必在
endElement中清空临时缓冲(如 StringBuilder),防止字符内容累积
启用 SXSSF 写入时的正确清理习惯
SXSSF 本身是写入端流式方案,但若用于“读—改—写”场景,仍可能因误用引发泄漏:
- 创建
SXSSFWorkbook时必须显式指定rowAccessWindowSize(如new SXSSFWorkbook(1000)),避免默认 100 行导致频繁刷盘和反序列化开销 - 每写入若干行(如 500 行)后调用
sheet.flushRows(n),将旧行真正刷出内存 - 写入完成后必须调用
workbook.dispose()(不只是close()),否则临时文件句柄和底层ZipOutputStream可能泄漏 - 确保
InputStream或FileInputStream被 try-with-resources 包裹,或显式close()
规避 POI 内部缓存与 Finalizer 陷阱
即使用了流式 API,若混用传统对象或未切断引用链,仍会触发隐性泄漏:
- 禁止在 SAX 处理器中缓存
Row、Cell或Workbook实例——它们不是流式 API 的一部分,一旦创建即引入 DOM 模式开销 - 不要调用
workbook.getSheetAt(0).getRow(i)类方法,这会强制触发完整 Sheet 加载 -
WorkbookFactory.create()默认返回XSSFWorkbook,大数据场景下必须显式用WorkbookFactory.create(inputStream, true)启用流式模式(POI 5.2+) - 避免全局静态持有
Workbook或CellStyle对象;POI 的CellStyle是有状态的,复用需谨慎
辅助手段:资源监控与兜底防护
技术选型和编码规范之外,运行时保障同样关键:
- JVM 启动参数加入
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./dump.hprof,便于事后用 MAT 分析泄漏源头 - 在解析循环中定期调用
System.gc()(仅作应急,不推荐长期依赖) - 对输入文件做前置校验:超出行数阈值(如 50 万行)自动切换到 SAX 模式,避免配置遗漏
- 使用
try-with-resources包裹所有流(OPCPackage、InputStream、XMLReader),确保异常时也能释放底层资源











