PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Wednesday, October 5, 2022

[FIXED] How to avoid memory leaks in PHP 5.4?

 October 05, 2022     memory-leaks, php, phpexcel     No comments   

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)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing