PHP Puts the Un in Unset

Recently I have seen—both in our code and that of others—the use of unset() in PHP as a means of reclaiming memory. I do not think this a good practice, and in my opinion we should not consider it part of our toolbox. Mainly because I worry it gives a false sense of aid, when in reality unset() rarely has any impact.

PHP is not a language that gives you fine-grained control over memory.

You do not have control over when variables are freed from memory, and you cannot force PHP to release memory. You can give hints to PHP, and unset() is such a hint, but that is it. If you are relying on unset() to help your memory problems, it is indictative that you already have a larger problem, which unset() is not going to solve.

I have seen unset() used in these ways most often:

  1. function foo() {
  2.     $bar = array();
  3.     $baz = some_function();
  4.  
  5.     // Lots of work here
  6.  
  7.     unset($bar);
  8.     unset($baz);
  9. }
  10.  
  11. function blah( $bar, $baz ) {
  12.     // Code code code
  13.     unset($bar);
  14.     unset($baz);
  15. }

In both cases the calls to unset() can be omitted with no change in memory usage. Profiling will demonstrate this, and you can easily write a quick script using memory_get_usage() to see the lack of difference for yourself. When you call unset() you are telling PHP that it is alright to free that variable. However, this is already the case for variables which go out of scope. In both functions above the $bar and $baz variables are no longer in scope at the functions’ end, and PHP flags them for garbage collection. Calling unset() here is redundant.

Note that this is not true when references are involved.

  1. function foo( &$bar ) {
  2.     // Do a bunch of stuff
  3.     unset($bar); // No effect
  4. }

Calling unset() on a reference _will not_ mark that variable for garbage collection because unset() cannot affect variables beyond the scope of its call point (with one exception). PHP will ignore the call to unset() above. Because $bar is a reference, it was not pushed onto the stack as a function argument, and there is not a temporary, lexically scoped $bar inside this function for PHP to reclaim – making this unset() a no-op.

This is true of globals unless you access them via $_GLOBAL, which is the above mentioned exception.

  1. function foo() {
  2.     global $bar;
  3.     unset($bar); // No-op
  4. }
  5.  
  6. function foo() {
  7.     unset($_GLOBAL[‘bar’]); // Has intended effect
  8. }

Semantically, unsetting a variable is on par with assigning it a null value. Assigning a null is ever so slightly faster, but can take up ever so slightly more memory; PHP maintains a reference count in memory on variables with null value, but unset() variables have their reference count removed immediately. Speed versus space trade-off, but I’m not sure of a situation where it would ever be meaningful.

PHP even lets you cast things to ‘unset’. I have never seen anyone do so, and I cannot imagine a reason for ever doing so. It has the same effect as casting to null. Interestingly, it is _not_ the same as calling unset() on a variable. This can be seen by observing how casting to ‘unset’ will not remove a variable from the symbol table.

  1. function foo($x) {
  2. }
  3.  
  4. function bar($x) {
  5.     unset($x);
  6. }
  7.  
  8. foo( (unset) ‘Lobby’ );
  9. bar( ‘Jones’ );

The above will print out
array(1) {
["x"]=>
NULL
}
array(0) {
}

The cast has no effect on the $x created within the scope of foo().
Curiously the cast has no effect within its own scope either.

  1. $foo = ‘Lobby’;
  2. bar( (unset) $foo );
  3. echo $foo;

The above will echo ‘Lobby’, as the cast does not unset $foo within the current scope. Or within the scope of the function call, as seen above. In this context ‘unset’ is a synonym for ‘null’. This fact can be stressed by pointing out that the cast is possible in places where unset() as an expression is a fatal error.

  1. foo( (unset) 10 ); // Works. Same as foo(null)
  2. unset( 10 ); // Fatal syntax error.

You cannot unset() literal values, but you can cast them as unset just fine. Good ol’ quirky PHP.

Related posts:

  1. Scoping in Javascript With ‘Let’
  2. Messing With Namespaces and Anonymous Functions
  3. Javascript: Notes on Scoping
  4. PostgreSQL Stored Procedures Part 1
  5. array_key_exists() versus isset()
0 Comment   |   Posted in PHP,Programming Languages,blog by EricR on October 21, 2009