PHP对象相互引用的内存溢出实例分析 |
本文标签:PHP,对象,相互引用,内存溢出 通常来说使用脚本语言最大的好处之一就是可利用其拥有的自动垃圾回收机制来释放内存 。你不需要在使用完变量后做任何释放内存的处理,因为这些PHP会帮你完成 。 问题症状如下: 如果两个对象之间存在着相互引用的关系,如“父对象-子对象”,对父对象调用 unset()不会释放在子对象中引用父对象的内存(即便父对象被垃圾回收,也不行) 。 是不是有些糊涂了?我们来看下面的这段代码: <? 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()) . " "; } ?> 运行这段代码,你会看到内存使用率越来越高越来越高,直到用光光 。 ...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 对大部分PHP程序员来讲这种情况不算是什么问题 。可如果你在一个长期运行的代码中使用到了一大堆相互引用的对象,尤其是在对象相对较大的情况下,内存会迅速地消耗殆尽 。 Userland解决方案 虽然有些乏味、不优雅,但之前提到的 bugs.php.net 链接中提供了一个解决方案 。 以下是“修复后”的代码: <? 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()) . " "; } ?> 注意那个新增的Foo::__destruct()方法,以及在释放对象前对 $foo->__destruct() 的调用 。现在这段代码解决了内存使用率一直增加的问题,这么一来,代码就可以很好的工作了 。 PHP内核解决方案 为什么会有内存溢出的发生?我对PHP内核方面的研究并不精通,但可以确定的是此问题与引用计数有关系 。 通俗的来说,大体意思是:一个引用计数没有递减,所以一些内存永远得不到释放 。 与其改变垃圾回收的过程,为什么不用 unset() 对内部对象做释放的工作呢?(或者在释放对象的时候调用 __destruct()?) 相信本文所述对大家深入理解PHP运行原理有所帮助 。 |