我刚刚发现了在PHP中泄漏内存的困难方法。我在循环中运行一些代码,并且在每个循环之后内存使用量增加,直到脚本达到内存限制。我已经确定了:
gc_collect_cycles()
这是一个示例脚本,演示了与PhpExcel library:
有关的问题require_once(__DIR__ . '/libraries/PHPExcel/PHPExcel.php');
ini_set('memory_limit', '200M');
@mkdir(__DIR__ . '/output');
gc_enable();
for ($n = 0 ; $n < 10 ; $n++)
{
do_it($n);
gc_collect_cycles();
}
function do_it($n)
{
echo 'Round '.$n.'...';
$text = str_repeat('x', 50000);
$phpexcel = new PHPExcel();
$worksheet = $phpexcel->getActiveSheet();
for ($r = 1 ; $r < 50 ; $r++)
for ($c = ord('A') ; $c <= ord('S') ; $c++)
$worksheet->setCellValueExplicit(chr($c) . $r, $text, PHPExcel_Cell_DataType::TYPE_STRING);
// $phpexcel->disconnectWorksheets();
unset($phpexcel, $worksheet);
echo 'done, now using ' . round((memory_get_usage()) / 1024 / 1024).' MB' . "\n";
}
输出:
Round 0...done, now using 41 MB
Round 1...done, now using 80 MB
Round 2...done, now using 123 MB
Round 3...done, now using 157 MB
Round 4...
Fatal error: Allowed memory size of 209715200 bytes exhausted (tried to allocate 36 bytes)
现在针对此特定问题the solution is在每个周期后调用$phpexcel->disconnectWorksheets();
,这会取消某些对象成员。
真正的问题是:作为PHP程序员,我应该做些什么来避免此类内存泄漏?在取消设置对象之前,我是否真的必须以递归方式遍历每个对象以取消其成员?
答案 0 :(得分:2)
这里的问题是静态数组PHPExcel_Calculation::$_workbookSets
获取对每个工作簿的PHPExcel_Calculation
对象的引用。每次do_it()
运行都会增长。因为对象永远不会超出范围,所以无法回收它们的内存和属性等等。
将unset(...);
替换为PHPExcel_Calculation::unsetInstance($phpexcel);
并且内存泄漏消失,因为这会从该阵列中删除关联的对象(仅那个。)
对于一般问题:循环引用不是问题,垃圾收集器处理它们就好了 - 避免全局(静态只是花哨的全局),因为它们可以很好地隐藏并且气球失控。