In PHP, the destructor is called as soon as all the references to the current object are removed. This logic of reference counting can fail when circular referencing occurs. Circular referencing is when different objects circularly refer to one another, or the same object refers to itself by using the keyword $this
.
The most reliable way to resolve circular referencing is to restructure your code. For example, if your classes depend on one another of some part of the code, then you can create a trait from this shared code and use it in the classes independently.
Memory leak by circular dependency can also be resolved by explicitly dereferencing objects after they have performed their duties. Let’s look at this in code below:
<?phpclass ClassA{public $dependencyLink;public function destroyLink(){echo "Destroying dependencyLink A\n";unset($this->dependencyLink);}public function __destruct(){echo "Destructor of A\n";}}class ClassB{public $dependencyLink;public function helloWorld(){echo "HelloWorld\n";}public function __destruct(){echo "Destructor of B\n";}}$obj1 = new ClassA;$obj2 = new ClassB;$obj1->dependencyLink = $obj2;$obj2->dependencyLink = $obj1;// Showing circular referencing$obj2->dependencyLink->dependencyLink->helloWorld();// $obj1->destroyLink();unset($obj1);unset($obj2);echo "I am here at end of script\n";?>
On executing the code, you will see the following output:
HelloWorld
I am here at end of script
Destructor of A
Destructor of B
Lines 2–15: We define a ClassA
with a public variable $dependencyLink
and two public methods destroyLink
and __destruct
.
Similarly we define a ClassB
on lines 17–29.
New objects of ClassA
and ClassB
are created on lines 31 and 32.
We create a circular referencing on lines 34 and 35, which caused this behavior.
Line 38: The object $obj2
calls its helloWorld
method via circular referencing.
The output shows that the objects were not unset
by lines 43 and 44, and their destructor methods were executed after completing the script.
Now, uncomment the line 40 and execute the code again. This time the output will be in order.
HelloWorld
Destroying dependencyLink A
Destructor of B
Destructor of A
I am here at end of script
destroyLink
method at line 40, we unset the dependencyLink
, resolving the problem of circular referencing.Another way to resolve circular referencing is by using the WeakReference
class of PHP. This allows us to create a reference that does not disrupt the destruction process.
Note:
WeakReference
class only works in PHP 7 7.4.0, PHP 8.
WeakReference::create($obj);
The create
method accepts an object and creates a weak reference against it.
<?phpclass ClassA{public $name = "A";public $circular_ref;public function __construct(){$this->circular_ref = WeakReference::create($this);}public function __destruct(){echo "Destructor of A\n";}}echo 'PHP version: ' . phpversion() . "\n";$obj1 = new ClassA;// Showing circular referencingecho $obj1->circular_ref->get()->name . "\n";unset($obj1);echo "I am here at end of script\n";?>
ClassA
to demonstrate the weak circular reference using its variable $circular_ref
.$this
.WeakReferenced
object7.4.1
. Otherwise, this would not have worked.Note:
WeakReference
objects cannot be serialized.