Collecting Garbage: PHP's take on variables
In this three part column I will explain the merits of the new Garbage Collection (also known as GC) mechanism that is part of PHP 5.3. Before we start with the intricate details of PHP's new GC engine I will explain why it is actually needed. This, combined with an introduction how PHP deals with variables in general is explained in this first part of the column. The second part will cover the solution and some notes on the GC mechanism itself, and the third part covers some implications of the GC mechanism, as well as some benchmarks. But now first on to the introduction.
PHP stores variables in containers called a "zval". A zval container contains besides the variable's type and value, also two additional bits of information. The first one is called "is_ref" and contains a boolean value whether this variable is part of a "reference set". With this bit PHP's engine knows how to differentiate between normal variables, and references. However, PHP has user-land references-as created by the & operator, but also an internal reference counting mechanism to optimize memory usage. The second piece of additional information, called "refcount", contains how many variables names-also called symbols-point to this one zval container. All symbols are stored in a symbol table, of which there is one per scope. There is a scope for the main script (ie, the one requested through the browser), as well as for every function or method.
A zval container is created when a new variable is created with a constant value, such as:$a = "new string";
In this case the new symbol name "a" is created in the current scope, and a new variable container is created with type "string", value "new string". The "is_ref" bit is by default set to "false" because no user-land reference has been created. The "refcount" is set to "1" as there is only one symbol that makes use if this variable container. Also, if the "refcount" is "1", "is_ref" is always "false". If you have Xdebug installed you can display this information by calling:xdebug_debug_zval('a');
which displays:a: (refcount=1, is_ref=0)='new string'
Assigning this variable to another variable name, increases the refcount:$a = "new string"; $b = $a; xdebug_debug_zval('a');
which displays:a: (refcount=2, is_ref=0)='new string'
The refcount is "2" here, because the same variable container is linked with both "a" and "b". PHP is smart enough not to copy the actual variable container when it is not necessary. Variable containers get destroyed when the "refcount" reaches zero. The "refcount" gets decreased by one for each symbol linked to the variable container leaves the scope (f.e. if the function ends) or when unset() is called on a symbol. The following example shows that:$a = "new string"; $c = $b = $a; xdebug_debug_zval('a'); unset($b, $c); xdebug_debug_zval('a');
which displays:a: (refcount=3, is_ref=0)='new string' a: (refcount=1, is_ref=0)='new string'
If we now call "unset($a);" the variable container, including the type and value will be removed from memory.
Things get a tad more complex with compound types such as arrays and objects. Instead of a scalar value, arrays and objects store their properties in a symbol table of their own. This means that the following example creates three zval containers:$a = array('meaning' = 'life', 'number' = 42); xdebug_debug_zval('a');
which displays (after formatting):a: (refcount=1, is_ref=0)=array ('meaning' = (refcount=1, is_ref=0)='life', 'number' = (refcount=1, is_ref=0)=42)
Graphically, it looks like:
You can see the three zval containers here: "a", "meaning" and "number". Similar rules apply for increasing and decreasing "refcounts". Below we add another element to the array, and set it's value to the contains of an already existing element:$a = array('meaning' = 'life', 'number' = 4
Truncated by Planet PHP, read more at the original (another 4743 bytes)