前言
早上九点多,群里说生产上出现OOM,也定位到了出现异常的代码,读取Excle导致的,问了Excle里面有六万多条数据,生产上先加大了堆内存然后重启服务
问题确定
因为我们已经能大概确定问题代码了,但是我们还是想确认一下
登录服务器,首先要查找到对应的java进程
ps -ef|grep java
发现服务启动的参数类似下面的,生产设置的堆内存最大为512M
-Xms50m -Xmx50m -XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=C:\Users\xxx\Desktop\temp
这就很舒服了,根据HeapDumpPath找到对应hprof,生成的hprof文件例如:java_pid9436.hprof,如果多个java进程的hprof文件都在一个目录下的话,要根据对应java进程的pid来获取到hprof文件
如果启动没有设置**-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=C:\Users\xxx\Desktop\temp**
因为这里已经能定位到问题,可以在本地上模拟出来,设置好启动的JVM参数就好了
这里借助MAT这一类的分析工具
可以看到有个超级大的对象占用了421.9M的内存
点击Dominator Tree 看看是谁占用
再根据前面已经定位到了出问题的代码,可以说明是由于解析数据量过多的Excle文件导致的此次OOM
本地重现
搞了个1万条数据的Excle,用读取Excle的问题代码直接读取,启动之前设置好JVM的参数
经过多次测试发现,堆内存要设置在500M以上,才能避免OOM,真的很吃惊,一万的数据量,竟然要花费至少512M的堆内存
具体原因
上网搜了一些资料,找到了问题所在
POI读取Excel有两种模式,一种是用户模式,一种是SAX事件驱动模式,将xlsx格式的文档转换成CSV格式后进行读取。用户模式API接口丰富,使用POI的API可以很容易读取Excel,但用户模式消耗的内存很大,当遇到很大sheet、大数据网格,假空行、公式等问题时,很容易导致内存溢出。POI官方推荐解决内存溢出的方式使用CVS格式解析,即SAX事件驱动模式
debug进去读取问题代码的Excle里去,发现使用的是UserModel
问题解决
- 可以自己写代码用SAX事件驱动模式处理
- 找一个比较好的开源(这里推荐alibaba的easyexcle)
我这里就选择了easyexcl(https://github.com/alibaba/easyexcel)
选择之前做了一下测试,直接在IDE上拉取代码,easyexcl仓库里面已经介绍了如何使用,这里我就不介绍了,直接测试,与前面的本地重现一下,设置好JVM,用的也是之前的Excle文件,经过多次测试,发现用至少用50M的堆内存,
发现使用的是SAX事件驱动模式处理的
处理
- 线上暂时加大堆内存
- 后期对处理Excle的代码进行优化,引入alibaba的easyexcle
总结
- dump快照 工具分析
- 定位到原因
- 提出解决方案
- 分析和验证解决
- 找到最合适的处理