Example Analysis of Memory Overflow of PHP Object Reference

  • 2021-07-16 02:03:32
  • OfStack

Generally speaking, one of the greatest benefits of using scripting languages is that they can use their automatic garbage collection mechanism to free up memory. You don't need to do anything to free up memory after using variables, because these PHP will do it for you.
Of course, we can call the unset () function as we wish to free up memory, but we don't usually need to.
In PHP, however, there are at least one case where memory is not automatically freed, even if you call unset () manually. For details, please refer to PHP official website's analysis of memory leakage: http://bugs.php.net/bug.php? id=33595.

The symptoms of the problem are as follows:

If there is a reference relationship between two objects, such as "parent-child", calling unset () on the parent object does not release memory that references the parent object in the child object (even if the parent object is garbage collected).

Are you a little confused? Let's look at the following code:


<?
phpclass Foo {
 function __construct(){
 $this->bar = new Bar($this);
 }
}
class Bar {
 function __construct($foo = null){
 $this->foo = $foo;
 }
}
while (true) {
 $foo = new Foo();
 unset($foo);
 echo number_format(memory_get_usage()) . " ";
}
?>

Run this code, and you will see that the memory utilization rate is getting higher and higher until it runs out.


...33,551,61633,551,97633,552,33633,552,696PHP Fatal error: Allowed memory size of 33554432 bytes exhausted(tried to allocate 16 bytes) in memleak.php on line 17

For most PHP programmers, this situation is not a problem. However, if you use a large number of objects that refer to each other in a long-running code, especially if the objects are relatively large, the memory will quickly run out.

Userland Solution

Although somewhat boring and inelegant, a solution is provided in the bugs. php. net link mentioned earlier.
This scheme uses an destructor method before releasing objects to achieve its goal. The Destructor method purges all internal parent object references, which means that this part of memory that would otherwise have overflowed can be freed.

The following is the code after repair:


<?
phpclass Foo {
 function __construct(){
 $this->bar = new Bar($this);
 }
 function __destruct(){
 unset($this->bar);
 }
}
class Bar {
 function __construct($foo = null){
 $this->foo = $foo;
 }
}
while (true) {
 $foo = new Foo();
 $foo->__destruct();
 unset($foo);
 echo number_format(memory_get_usage()) . " ";
}
?>

Notice the new Foo:: __destruct () method and the $foo- > The call to __destruct (). Now this code solves the problem of increasing memory usage by 1, so that 1, the code can work well.

PHP kernel solution

Why is there a memory overflow? I am not well versed in PHP kernel research, but what is certain is that this problem has something to do with reference counting.
The reference count of $foo in $bar is not decremented by the release of the parent $foo, when PHP thinks you still need the $foo object and does not release this part of memory. The principle is roughly the same.

Generally speaking, the general meaning is: 1 reference count does not decrease, so 1 memory will never be released.
In addition, the aforementioned bugs. php. net link points out that modifying the garbage collection process will sacrifice a great deal of performance, which needs readers' attention.

Instead of changing the garbage collection process, why not use unset () to do the work of releasing internal objects? (Or call __destruct () when the object is released?)
Perhaps PHP kernel developers can modify this garbage collection mechanism here or elsewhere.

I believe this article is helpful for everyone to deeply understand the operation principle of PHP.


Related articles: