最近被分到了一个关于百万级excel导出内存优化问题,然后就去研究了做法。
1,
excel导出工具包,找了几个,然后使用了阿里包装的easyExcel,很多博客都说不错
pom:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>1.1.1</version>
</dependency>
2,
相关代码:
这个代码目前是写死的,测试100W的数据量,后续如果有需求的话,可以继续做优化(比如分页获取,对excel进行zip压缩等等),我这个算是压力测试
3,DTO类包装
在这里执行对base类的继承和对excel操作的相关优化,必须要使用继承类
4,实践调优
代码的执行步骤是,
1,一百万条数据基础数据(大量占内存)
2,基础数据转换为继承base的数据(进一步消耗内存)
3,base数据转换成excel数据(excel对象消耗内存)
使用JProfiler对内存变化进行验证
第一条验证:
mybatis获取和包装100W条数据的时候内存占有量持续上升,在对mybatis调用完之后,空闲内存还有142MB的使用情况时,jvm进行了自动扩容,同时,对jdbc的读取后进行了一次自动的gc回收,可以看出来,第一条确实是非常的占据内存的
优化方案:采取分页的模式进行读取数据,每次读取完后都会清空内存,降低一次性读取内存的压力
第二条验证:
我们会发现第二条内存的变化比从数据库获取数据占用量更大,因为可能一些数据还没来得及全部清掉,但是也是有限,不过这个数据量过大更多的是因为第一条的方案有问题而导致,所以使用第一条方案这条的内存也能大大降低
第三条验证:
这一次内存的变化是excel进行输出转换的时候,我发现内存并没有很明显的增高,由此可见esayExcel的作用还是很明显的,内存仅仅只是增加了3Mb,这一步操作并不会造成内存的增加
总结:这个工具包的好处就在于,我们容易去进行操作和优化的地方,可以很好的支持我们去操作和优化,业务状态灵活,而包的功能已经优化过了,不会增加我们的的负担
以及一些备注说明:
这里可以探讨一下gc的垃圾回收机制
1,自动触发
2,手动触发
手动触发没什么好说的,自动触发的时候我查了很多资料,都没有关于说是gc是定时触发,没有评率可言引用以为网友的话
但是我在这次测试的过程中发现,在内存压力比较大的情况下都会发生自动自动回收:
我研究这个是因为,我发现我某次请求结束后,并没有发生自动回收的情况出现,然后内存占用会比请求之前多出很多,而我查了内存占用情况也发现了很多arrays的存在。我在想我要不要手动调用一次system.gc()的时候,我重新发起了一次请求,我发现这次请求在请求的过程中,内存数据持续增加,但是却在mybatis读取结束之前就进行了自动回收。1.8的回收功能比较自动,所以我觉得代码编写确实没有内存溢出的情况的下,确实是没必要考虑去进行手动要求触发回收功能
最后,JDK1,8的特性里是没有永久区的,同事也不再需要对老年区和青年区去进行分区,在堆内存不足的情况下,会进行自动扩容,但是扩容后是不会自动去主动释放空闲内存的,所以对于一些高内存占用的任务,最好加上限制访问量锁等优化方式。毕竟很少有项目会出现任意让客户去调用这种可能出现高内存占用的方法,一般要调用也是后台运营使用,限制调用就还是很有必要的。
有错误欢迎及时指出