Where to find the "low memory" and "free CPU cycles" calls triggering garbage collection on unset()?

StackOverflow https://stackoverflow.com/questions/20230626

  •  05-08-2022
  •  | 
  •  

Question

I often find references to the following quote being used when explaining that a PHP unset() doesn't trigger "garbage collection" immediately, but only when it sees fit (emphasis mine):

unset() does just what it's name says - unset a variable. It does not force immediate memory freeing. PHP's garbage collector will do it when it see fits - by intention as soon, as those CPU cycles aren't needed anyway, or as late as before the script would run out of memory, whatever occurs first.

If you are doing $whatever = null; then you are rewriting variable's data. You might get memory freed / shrunk faster, but it may steal CPU cycles from the code that truly needs them sooner, resulting in a longer overall execution time.

I want to know how the C code for this "low memory" and "free CPU cycles" triggering of the garbage collection works exactly and whether it differs between PHP 5.2.x and PHP 5.3+.

So I downloaded the C source files of PHP 5.2.17 and tried to locate the proper code sections. Maybe I'm just blind, or my C skills are too low, but I failed to find such code.

Can somebody point me to the proper C files, please?

EDIT:

While searching for example usages of the above quote, I realized something weird.

Some of the examples, like https://stackoverflow.com/a/584982/693207, reference to this quote by using the following URL to a comment on php.net: http://us2.php.net/manual/en/function.unset.php#86347.

Browsing to this URL only shows the top of the unset() manual. Comment #86347 is missing.

Checking the wayback machine shows, that this comment DID exist from Oct 2008, but vanished sometime in or after Sep 2012 (reason unknown).

Maybe that quote is, and always was, just plain wrong?

Or is there anybody out there, who can point me to the proper C files?

Était-ce utile?

La solution

OK, so it's time for some PHP Mythbusters! Please start by reading the PHP documentation on how garbage collection works, as I'm going to assume some prior knowledge of how this all works:

The second document specifically explains what triggers the running of the cyclic garbage collector. It has nothing to do with "free CPU cycles" or "low memory" — it's based entirely on the number of potential garbage objects that are present:

When the garbage collector is turned on, the cycle-finding algorithm as described above is executed whenever the root buffer runs full. The root buffer has a fixed size of 10,000 possible roots.

That is, the cyclic garbage collector runs any time that a certain number of potentially garbage objects have accumulated, irrespective of the size of those objects. Reviewing the code in zend_gc.c confirms this — there is certainly nothing in there checking the overall amount of free memory, and there's certainly none of the threading that would be needed to make the GC run while the CPU is free. So I think we can call this part "busted".


Next, let's take a look at what the actual difference between $x = null and unset($x) might be. First, let's confirm that they do the same thing using this class as our Buster the Test Dummy:

class NoisyDestructor {
    function __destruct() {
        print "Destructor called\n";
    }
}

Now, let's see what the difference between setting a variable to null and unset()-ing it is:

$x = new NoisyDestructor();
print "Created\n";
$x = null;
print "Nulled\n";

print "\n";

$x = new NoisyDestructor();
print "Created\n";
unset($x);
print "Unset\n";

When we run this, we see:

Created
Destructor called
Nulled

Created
Destructor called
Unset

Wait a minute — that's the exact same sequence for both! There's no functional difference between the two. Now, how about the performance?

class Thing { }

$start = microtime(true);
for ($i = 0; $i < 1e6; $i++) {
    $x = new Thing();
    $x = null;
}
printf("%f sec for null\n", microtime(true) - $start);

$start = microtime(true);
for ($i = 0; $i < 1e6; $i++) {
    $x = new Thing();
    unset($x);
}
printf("%f sec for unset\n", microtime(true) - $start);

Now, using my laptop to test, with PHP 5.4, I get:

0.130396 sec for null
0.175086 sec for unset

Not only is the performance difference between setting a variable to null and unsetting it pretty minor, considering how many times we had to run this loop to see this result, but it's actually the exact opposite of what that comment claimed: unset() is about 25% slower! This PHP myth has been well and truly busted.

BUSTED

TL;DR: The quote you found is completely wrong. (It seems likely that it was removed from PHP.net for this exact reason.)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top