POI直接加载Excel文件导致的线上OOM问题

项目中为了获取用户导入的模板属性, 需要直接读取用户上传的excel文件,创建XSSWorkbook对象, 用户上传了一个12M大小的文件, 导出项目读取Excel时占用内存达到3G, 导致了项目OOM, 然后重启

分析过程:

1, 使用Java VisualVM加载dump文件, 查询对象占用内存

 查看占用较多内存传对象是 org.apache.xmlbeans.impl.store.Xobj$AttrXobj, org.apache.xmlbeans.impl, 查资料发现这两个对象是POI中的对象, 初步定位为POI进行导入导出操作时导致的内存溢出

2, 使用MemoryAnalyzer加载dump文件, 查看Thread Stack线程栈信息

 查询线程栈信息, 依旧指向的是POI中的代码, 查看下面几行就指向了业务代码, 找到代码中指向的行, 代码如下:

 这里使用了POI的userModel模式, 直接加载excel文件到内存中创建XSSFWorkbook对象, 占用内存很大, 极有可能造成内存溢出

联系用户, 获取到了用户导入的Excel文件, 文件有12M, 然后在本地复现内存溢出的场景,

在idea中Add VM Options设置启动内存和最大内存占用:

 

在Excel导入接口中上传用户的12M文件, 用Java VisualVM监测内存占用情况:

debug到下面这行代码时, 出现内存和CPU占用飙升, 控制台打印报错OutOfMemoryError

XSSFWorkbook xssfWorkbook = new XSSFWorkbook(templateFile.getInputStream())

 控制台打印如下:

 查资料了解了POI的userModel占用内存较大

1、usermodel方式,就是我们一般使用的方式,这种方式可以读可以写,但是CPU和内存消耗非常大
2、eventmodel方式,基于事件驱动,SAX的方式解析excel(.xlsx是基于OOXML的),CPU和内存消耗非常低,但是只能读不能写,塔式应用程序一边读取数据,一边处理数据。 

SAX模型最大的优点是内存消耗小,因为整个文档无需一次加载到内存中,这使SAX解析器可以解析大于系统内存的文档。

SAX的工作原理简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,
由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束
 

最后修改获取Excel文件的属性代码如下:

 总结主要是通过两点防止导入Excel文件导致的OOM

(1) 将接口上传文件大小的限制降低为5M

(2) 不要使用POI中的usermodel方式读取Excel文件(API简单但占用内存极大), 使用eventmodel方式, SAX读取Excel文件比较解决内存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值