最近我们公司的项目的在导出excel的时候偶尔出现内存溢出错误,经过测试发现当数据量大于5000条就出现这个问题(默认php.ini memory 是128M)


Allowed memory size of 134217728 bytes exhausted (tried to allocate 43 bytes)


解决内存溢出问题方法如下



方案1 和 方案2 算是非常简单粗暴的了,但是没有触及根本,就是为什么会内存溢出,作为技术人咱们就应该打破砂锅问到底了


本文主要简单描述下我是如何排查内存溢出问题并如何修复的

操作步骤

业务大致逻辑

① 查询数据库取出所有符合条件的数据

② 循环符合条件的数据,进行数据处理,并放入一个新数组中

③ 使用PHPEXCEL库生成excel表格

Debug

借助php的memory_get_usage函数查看内存使用情况


断点1:8M 
业务逻辑①
断点2:47M
业务逻辑② 
断点3:82M
业务逻辑③
断点4:140M


从上图我们可以得出如下结论


业务逻辑① 消耗内存:39M = 47M - 8M

业务逻辑② 消耗内存:35M = 82M - 47M

业务逻辑③ 消耗内存:58M = 140M - 82M

优化业务逻辑③

从业务逻辑描述看,业务逻辑①是优化不掉的,查询数据库系统必然开销了。我一开始想到的是优化业务逻辑③

业务逻辑③:主要是调用第三方库PHPEXCEL生成表格,通过google了下发现主要都是通过缓存解决这个问题,PHPEXCEL作者本身就考虑到了这个问题,大家可以查询这边文章 phpExcel大数据量情况下内存溢出解决  


断点1:8M 
业务逻辑①
断点2:47M
业务逻辑② 
断点3:82M
业务逻辑③
断点4:117M


这样之后,内存使用情况

业务逻辑① 消耗内存:39M = 47M - 8M

业务逻辑② 消耗内存:35M = 82M - 47M

业务逻辑③ 消耗内存:35M = 117M - 82M

优化业务逻辑②

业务逻辑②只是简单的遍历进行数据处理,并放入到新的数组中去。这也就明白了,大数组引起的内存开销,也是没办法避免的。但是我中午在考虑着问题的时候发现了一点,对于没用的变量我们要及时注销(unset),这样我就发现业务逻辑① 结果集变量经过遍历之后就没有用啦,可以直接注销掉,这样优化之后 内存使用情况如下


断点1:8M 
业务逻辑①
断点2:47M
业务逻辑② 
断点3:31M
业务逻辑③
断点4:67M

 

这样之后,内存使用情况


业务逻辑① 消耗内存:39M = 47M - 8M

业务逻辑② 消耗内存:-16M = 31M - 47M

业务逻辑③ 消耗内存:36M = 67M - 31M

成果

经过优化 最后只是用67M内存,就算是默认的128M限制 我们都可以很好的支持了 ~~,最后其实我们使用了三种方案的结合体

Game Over

经过仔细排除优化,其实发现做这类刨根问底的事情也是蛮有成就感的,并且也算是真真在慢慢深入底层了,关于php的引用计数 和 写时复制希望大家都可以了解下

参考资料

phpExcel大数据量情况下内存溢出解决: http://www.cnblogs.com/myx/archive/2013/05/20/phpExcel-setCache.html 

PHP扩展开发及内核应用:http://www.cunmou.com/phpbook/