如何避免PHP 5.4中的内存泄漏?

时间:2015-09-14 07:36:38

标签: php memory-leaks phpexcel

我刚刚发现了在PHP中泄漏内存的困难方法。我在循环中运行一些代码,并且在每个循环之后内存使用量增加,直到脚本达到内存限制。我已经确定了:

  • 没有全局变量(我相信也没有静态变量)
  • 我正在运行PHP 5.4,据说它有一个新的垃圾收集器用于循环引用
  • 每个周期后我的所有变量都超出了范围
  • 我在每个周期后调用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程序员,我应该做些什么来避免此类内存泄漏?在取消设置对象之前,我是否真的必须以递归方式遍历每个对象以取消其成员?

1 个答案:

答案 0 :(得分:2)

这里的问题是静态数组PHPExcel_Calculation::$_workbookSets获取对每个工作簿的PHPExcel_Calculation对象的引用。每次do_it()运行都会增长。因为对象永远不会超出范围,所以无法回收它们的内存和属性等等。

unset(...);替换为PHPExcel_Calculation::unsetInstance($phpexcel);并且内存泄漏消失,因为这会从该阵列中删除关联的对象(那个。)

对于一般问题:循环引用不是问题,垃圾收集器处理它们就好了 - 避免全局(静态只是花哨的全局),因为它们可以很好地隐藏并且气球失控。