Q:

How to avoid memory leaks in PHP 5.4?

I just found out the hard way that it is possible to leak memory in PHP. I'm running some code in a loop and after each cycle the memory usage increases until the script hits the memory limit. I already made sure:

There are no global variables (and I believe no static either)
I'm running PHP 5.4 which supposedly has this fancy new Garbage Collector for cyclic references
All my variables go out of scope after each cycle
I am calling gc_collect_cycles() after each cycle

This is an example script that demonstrates the problem in regard to the 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";
}

Output:
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) 

Now for this particular problem the solution is to call $phpexcel->disconnectWorksheets(); after each cycle, which unsets some object members.
The real question is: What am I, as a PHP programmer, supposed to do to avoid such memory leaks? Do I really have to recursively traverse each object to unset its members before I can unset the object?

A:

The problem here is that the static array PHPExcel_Calculation::$_workbookSets gets a reference to the PHPExcel_Calculation object for each workbook. Every time do_it() runs this grows. Since the objects are therefore never really out of scope, their memory and that of their properties and so on cannot be reclaimed.
Replace your unset(...); with PHPExcel_Calculation::unsetInstance($phpexcel); and the memory leak goes away, as this removes the associated object from that array (and does only that.)
To the general question: cyclic references aren't the issue, the garbage collector handles them just fine -  avoid globals (statics are just fancy globals) as they can hide well and balloon out of control.

