发布于2026-07-08 阅读(0)
扫一扫,手机访问
XSSFWorkbook 容易 OOM 是因为它构建了全量 DOM 树,而事件驱动(SAX)只做流式解析,内存占用只有几百 KB。必须要用 XSSFReader + SharedStringsTable + StylesTable 这三件套,而且顺序和初始化时机有严格的限制。

问题就出在这儿——XSSFWorkbook 会把整个 .xlsx 解压后,在内存里完整构建一棵 DOM 树。一个 200MB 的 Excel 文件,内存占用轻松突破 1.5GB,这谁顶得住?而事件驱动的 SAX 模式就不干这事儿,它不建树、不缓存全量数据,只在解析到某行或某个单元格的时候才触发回调,内存常驻只有几百 KB。本质上,它是把 Excel 当作 ZIP + XML 流来处理,靠 XSSFReader 和自定义 DefaultHandler 来驱动整个解析流程。
缺一个都不行。少一个,轻则文本错位、格式跑偏,重则直接崩掉。
XSSFReader 是入口,负责打开 OPCPackage 并拿到 sheets.xml 和 sharedStrings.xml 的流SharedStringsTable 存着所有字符串的字面值(Excel 为了省空间,会把重复的字符串统一索引),不传它,t 标签里的内容根本拿不到真实值StylesTable 提供单元格的样式和数据类型(比如日期、数字格式),少了它,所有单元格默认都当成字符串处理,像 DateUtil.isCellDateFormatted(cell) 这类判断直接失效这里有个关键提醒:不要用 OPCPackage.open(file) 直接传文件路径,要传 FileInputStream,还得确保它没有被其他线程关闭。另外,SharedStringsTable 必须在 XSSFReader 实例化之后马上构建,顺序错了就会空指针异常。
Excel 单元格的 s(style index)、t(type)、v(value)这三者组合决定了最终内容,常见的坑点如下:
t 属性,但 v 里存的是浮点数原始值(比如日期存为 44562.0),得用 DateUtil.getJa vaDate(double) 来转换t="s",这时 v 是 shared-strings 表的索引,必须去查 SharedStringsTable 才能拿到原文本t="str" 或 t="n",但 v 是计算结果,不是公式本身。如果想看原始公式,得额外监听 f 标签v 标签,也没有 t,这时候应该返回 null 或空字符串,千万别强行调用 toString()来看个实际代码片段:
if ("s".equals(nextDataType)) {
String s = stringsTable.getItemAt(Long.parseLong(value));
row.add(s != null ? s : "");
}
POI 默认会尝试从样式表向上追溯父样式,在大规模 sheet 里,这会引发大量无效对象的创建。所以必须显式关闭:
XSSFReader 后,调用 reader.setStylesTable(null) 是不对的——这会导致样式丢失。正确的做法是传入一个精简后的 StylesTable,并确保它的内部没有启用 isStyleInheritanceEnableddataFormatter = new DataFormatter(Locale.ROOT); dataFormatter.setUseCachedValuesForFormulaCells(false); 来避免缓存污染startElement 里做任何 IO 或者复杂对象的构建,所有业务逻辑都延后到整行收尾(endElement 触发 row 完整之后)再处理真正压测下来,单核 CPU 处理 100 万行耗时大约 8–12 秒,内存峰值稳定在 30–60MB。最容易忽略的其实是 SharedStringsTable 的构建时机和 StylesTable 的裁剪粒度——这两点没控好,哪怕是用事件模式,照样 OOM。
上一篇:怎么利用 StringJoiner 在 Java 8 中优雅地构建带前后缀的 CSV 格式变量字符串
下一篇:怎么利用 Collections.unmodifiableNavigableMap() 创建一个完全只读且支持范围导航的映射
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8