Issue
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?
Solution
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.
Answered By - user3942918 Answer Checked By - David Goodson (PHPFixing Volunteer)
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.