Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When does PHP run garbage collection in long running scripts?

I am writing a PHP cli program that is a worker process for a queue system.

I assumed that PHP collects garbage from time to time in such a case, and won't constantly hit the memory limit.

This appears to not be the case.

Notes

  • Running on PHP 7
  • Its a long running script
  • zend.enable_gc = 1
  • There are no global variables
like image 819
mcfedr Avatar asked Aug 09 '16 12:08

mcfedr


1 Answers

It comes down to this. You have to trigger garbage collection manually by calling gc_collect_cycles().

I have written a bunch of code to try and track this down and come down to two scripts:

This one doesn't crash:

for($i = 0;$i < 100;$i++) {
    useMemory();
    gc_collect_cycles();
}

And this one crashes:

for($i = 0;$i < 100;$i++) {
    useMemory();
}

Here is a link to compare these scripts on Blackfire

As you can see, when you don't call gc_collect_cycles it never happens, and you get to the memory limit, and PHP kills itself.

PHP doesn't even use this opportunity to GC itself. The reasoning behind this is discussed on the PHP-DEV mailing list, but basically comes down to complications of how to run __destruct methods, that require memory, when the memory limit has been reached. (Also on the bug tracker #60982).

Memory usage func:

This is the code I used to 'waste' memory, it purposefully creates cyclic references that can only be cleaned by the garbage collector. Note that without these cycles the objects will be cleaned by reference counting as soon as they fall out of scope.

class Big {
    private $data;
    public function __construct($d = 0) {
        for($i = 0;$i< 1024 * 10;$i++) {
            $this->$i = chr(rand(97, 122));
        }
    }
}

function useMemory() {
    $a = new Big();
    $b = new Big();

    $a->b = $b;
    $b->a = $a;
}
like image 131
mcfedr Avatar answered Oct 05 '22 22:10

mcfedr