In this article I would like to share some of my professional experience on a couple of major issues that I am sure lots of my fellow PHP developers have to deal with sooner rather than later. By trial and error it helped me to come up with a working solution of how to go around it. Anyway…Let’s get started.
There is an issue of memory leak that PHP developers often experience in their daily work, although they rarely pay proper attention to it. Once an extremely convenient mechanism of transfer objects by reference has been added to version of PHP language, another problem of circular references emerged. But first things first.
How does the interpreter provide Reference counting?
In order to answer this question it is absolutely necessary to dig deeper to fully comprehend how PHP stores variables. For this purpose it utilizes a special container called zval. It supports a type and a value of a variable as well as two additional elements. The first element is called “is_ref”, it is Boolean and it indicates whether or not the variable is a reference. The second element is called “refcount” (Reference counting) and it indicates a number of variables containing references to the current variable. All the variable names are stored in name tables separately for each scope. There is a scope for the entire script as well as for each function or method. Every time you create a variable or an object, a separate zval container is produced for it.
$tmp = ‘Test’;
This record means that we create a $tmp variable in a global scope and assign a ‘Test’ value to it. Additionally it will contain is_ref = false parameters because no user reference has been created, and refcount = 1 because only one variable name refers to this container!
I.e. if refcount = 1 then is_ref is always false.
Now if we set this variable to another one, for instance, $tmp1 = $tmp; then we’ll have (refcount = 2, is_ref = 0) refcount parameter value increased by one because now two variables refer to the same container. It would be also useful to learn in which cases the Reference counting value decreases. This occurs when the variable stops referring to the container or it is beyond the scope of the container, or apparently it happens with the help of unset() function.
A variable is completely deleted from the memory only when nobody refers to it anymore.
This is only true for the objects that are stored in memory. When nobody refers to the object, the interpreter deletes it from the memory, otherwise it will exist until the end of the script, and will be deleted randomly (garbage collector is responsible for this part).
Now, I’d like to mention a few words about composite data types. Unlike scalar, they keep their properties in their own name tables, and virtually every array contains as many containers as there are variables in it, including itself:
$tmp = [‘a’ => 1, ‘b’ => 2];
And it creates three zval containers at once!
tmp: (refcount = 1, is_ref = 0) = array(
‘a’ => (refcount = 1, is_ref = 0) = 1,
‘b’ => (refcount = 1, is_ref = 0) = 2)
For the work with zval container by increasing the references and by their decreasing there are rules according to each data type described above.
Now let’s get back to the description of the initial problem that appears whenever one of the containers has a reference to itself, creating a circular reference this way. For instance, we can experience it during composition of classes or aggregation with the use of dependencies introduced (DI). In such cases it’s worth keeping in mind that you can only fully delete an object from the memory when nobody refers to it anymore, i.e.
public $instance = null;
public function __construct()
$this->instance = new CTest();
$test = new CTest();
If you do unset($test), it won’t be deleted from the memory and you will only lose an ability to refer to it, but it will consume space in the memory until you delete the property referring to it, e.g. here you should have done the following:
Or add deletion of all the properties, which contain references to this object, into the destruct before deleting it.
This case is also possible when there are child elements referring to parent elements. You should be extremely careful with circular references and explicit deletion of objects from the memory in long term scripts that are done as ‘daemons’, or where request never ends. Hopefully this piece of writing shed a little light on such a sensitive topic as a ‘Reference counting mechanism’ and memory leak with circular references work. For all it’s worth, it totally worked for me and everyone in my team. So,