Here's my code:
class a
{
public function __construct()
{
$this->test = function() {};
}
public function __destruct()
{
echo "called\n";
}
}
while (true) {
$a = new a;
$new = memory_get_usage();
if (isset($old)) {
echo ($new - $old) . "\n";
}
$old = $new;
}
Using that code every time that loop runs an additional 736 bytes of memory are consumed. Reason being that the destructor for the object is never called.
If I replace $a = new a;
with $a = function() {};
then no additional memory is consumed on each loop ad the constructor is called every time as well.
If I replace $this->test = function() {};
with $this->test = WeakReference::create(function() {});
the that seems to resolve the issue but then I'd have to rewrite all my code to do $this->test->get()(...)
instead of just ($this->test)('...')
or some such.
Is there another way to plug this memory leak that would require less rewriting than WeakReferences would require?
When you have normal references, you're creating a cyclical reference, because the object has a reference to the function, and the function is a closure that references the scope of the object. Cyclical references are a problem for garbage collecting based on reference counts, so the memory isn't reclaimed immediately when the variable is reassigned.
The objects and functions will be reclaimed periodically by the full garbage collector. You can force this by calling gc_collect_cycles()
.