Historically memory leaks have not been a huge concern for PHP development. Since PHP processes exit after processing a request all used memory is deallocated. But that doesn’t mean we are free to not care about memory management altogether. If your application is processing several requests at the sime time, the total amount of wasted memory may add up to a hefty number.
With projects like ReactPHP become more popular and widely used PHP is entering the domain of permanently running processes and we all look forward to using those to speed up our applications. But for that to become a reality our applications and frameworks have to be carefully rewritten to prevent memory leaks, since even the smallest one can grow to eat all available memory in a span of a month.
PHP utilizes reference counting to decide when a object can be removed. Eachtime you assign an object to a new variable its refcount is increased by one
$a = new stdClass; $c = $b = $a; xdebug_debug_zval('a'); //refcount = 3 unset($b, $c); xdebug_debug_zval('a'); //refcount = 1
When a refcount reaches 0 the memory is deallocated. This is the first thing you have to think about when instantiating multiple objects, e.g. your domain entities. You have to make sure that the references to them don’t hang around after they are no longer needed. One good rule of thumb for doing so is avoiding global state as much as possible. If all your object references are inside some local scope, it’s much easier to keep track on whats happening to them than when they are scattered all over the place.
Now let’s see wht happens here:
$a = new stdClass; $b = new stdClass; $a->b = $b; $b->a = $a; xdebug_debug_zval('a'); //refcount = 2 xdebug_debug_zval('b'); //refcount = 2 unset($a, $b);
After we unset both $a and $b variables we have no way of accessing those objects from our application anymore, but their refcount is not 0. Since what was $a still references $b and vice versa both their refcounts are 1, and they won’t be removed from memory. And that is what a memory leak is. In a perfect world we would try to avoid this as much as possible, but this might be hard to achieve. PHP tries to detect such unreachable objects using it’s garbage collector. When it’s called it looks through the memory for lopps in object references and deallocates them, thus saving us memory. But this process is not free, running it too often may slow down the execution time. The PHP manual states the impact is about 7% which is quite considerable.
I have found a nice trick that works for me to both have gc collection and get an almost insignificant performance impact: to run the garbage collection only after the end of each request. You can do it like so:
gc_enable(); gc_collect_cycles(); gc_disable();
Basically we are enabling garbage collection, running it once and disabling it again. This way we ensure that it runs only a single time per request.