项目场景:
项目场景:需求是需要导出系统的业务数据,然后发送邮件给业务部门,在系统查询出数据并开始发送过程中邮件出现内存溢出的问题
问题描述:
该发送邮件的功能是通过xxl跑定时任务,线上定时任务报错的堆栈信息如下,可以看到问题是内存溢出了,看到这个报错,我初步怀疑是发送邮件的时候数据量太大导致的
之后根据上面定位到的报错行数是在业务类的246行出现内存溢出的报错,该方法是poi的一个方法,作用就是将workbook中的数据读入ByteArrayOutputStream中,这时如果workbook对象的数据量较大,就会导致写入操作占用内存较大。
之后我就将生产的数据导到本地做一个测试,发现发送的excel数据量在15M大小左右,且workbook的,然后用java自带的内存工具进行监控,发现内存还是会突然上升到2g,同时因为线上的服务器环境内存比较紧张,每个服务限制的内存大小都是2g,所以会导致内存溢出的问题。
解决方案:
开始在网上找解决方法,了解到SXSSFWorkbook这个对象,可以处理大数据量的excel导出,实际如果数据量大于1万就可以使用这个对象进行导出了。因为公司的项目使用的是easypoi第三方包进行excel导出,easypoi已经包括了大数据量导出的api。
easypi的官方文档可以参考http://easypoi.mydoc.io/
下面贴下改造后的代码:
(1)数据通过exportBigExcel和iExcelExportServer分批生成workbook中,这时生成的对象已经是SXSSFWorkbook对象了
(2)iExcelExportServer的实现如下
(3)打断点进去调试,已经能发现生成的对象是SXSSFWorkbook,这个对象优化了对大数据量处理的方式,它的原理很简单,用硬盘空间换内存(就像hashmap用空间换时间一样)。 SXSSFWorkbook是streaming版本的XSSFWorkbook,它只会保存最新的excel rows在内存里供查看,在此之前的excel rows都会被写入到硬盘里(Windows电脑的话,是写入到C盘根目录下的temp文件夹)
(4)之后开始在本地进行调试,发现这时内存已经降下来了,现在顶峰是到1g左右,暂时解决了生产内存溢出的问题。