POI 内存溢出故障排查
发现问题
今天早上收到业务方反馈(尴尬),后台(gc-bootdo)非常慢,运维发现问题后,发现服务器CPU很高,Tomcat也在频繁FullGC,机智的运维同学第一时间dump tomcat的堆内存。
分析问题
从运维反馈的情况,应用服务器频繁FullGC,初步推断是堆内存满了且存在大量的对象无法被回收。以下通过使用MAT分析dump文件,具体步骤如下:
导入dump文件到mat,并使用Leak Suspects进行分析:
从上可以看出线程http-nio2-999-exec-78占用了大部分的本地变量,点击See stacktrace继续查看
从线程栈信息我们已经可以初步定位到问题的入口了,进一步我们再看一下具体占内存的对象,点击下图左上方红框的小图标。
其中ChatroomMessageDo为主要导出的业务对象,从上图可以看到,我们导出的对象实际个数为38w多,占用的堆内存大小为170m左右,那么问题出现在哪里呢?很明显应该是在导出功能使用的POI组件上,结合官方文档,我们可以看到官网建议在导出大量导出数据时,使用SXSSFWorkbook而不是XSSFWorkbook。
解决问题
从上述分析,我们使用XSSFWorkbook存在内存溢出问题,在解决问题之前,我们得有工具进行量化,记录优化前和优化后的指标,通过指标的对比,能够更好的体现优化效果。这里使用的量化工具是JUnit+IDEA+VisualVM(工具的使用就不细述了,自行百度安装)。
通过Run With VisualVM执行以上测试代码,可以从VisualVM中获得对应的堆栈信息
优化前(导出20w数据):
从上图可以看出,JVM堆栈的大小是随着导出的数据量(写入excel的数据量)线性增长的,堆最大使用了2G左右。
优化后(导出20w数据):
我们将SSFWorkbook改为XSSFWorkbook后,得出以上测试结果,堆最大的使用大小为860m左右。那么问题来了,修改后的堆大小是否还会随着数据量的大小接着线性的增长呢?我们把测试数据量加到100w试试。
优化后(导出100w数据):
可以看到内存占用的大小并不会随着数据量的增大而线性增大,随着垃圾回收,堆内存的使用大小逐渐减少。
结论
在导出数据量比较大的数据时应使用SXSSFWorkbook,并配置好缓冲大小。
参考:http://poi.apache.org/components/spreadsheet/how-to.html#sxssf
版权声明: 本文为 InfoQ 作者【Season】的原创文章。
原文链接:【http://xie.infoq.cn/article/1b8779412ff8fc5fb7c6eb6cc】。文章转载请联系作者。
评论