PHP对象相互引用的内存溢出
2011-01-07 14:00
211 查看
使用脚本语言最大的好处之一就是可利用其拥有的自动垃圾回收机制(释放内存)。你不需要在使用完变量后做任何释放内存的处理,PHP会帮你完成。
当然,我们可以按自己的意愿调用 unset() 函数来释放内存,但通常不需要这么做。
不过在PHP里,至少有一种情况内存不会得到自动释放,即便是手动调用 unset().
问题症状
如果两个对象之间存在着相互引用的关系,如“父对象-子对象”,对父对象调用 unset() 不会
释放在子对象中引用父对象的内存(即便父对象被垃圾回收,也不行)。
有些糊涂了?我们来看下面的这段代码:
[/code]
运行这段代码,你会看到内存使用率越来越高越来越高,直到用光光。
[/code]
对大部分PHP程序员来讲这种情况不算是什么问题。
可如果你在一个长期运行的代码中使用到了一大堆相互引用的对象,尤其是在对象相对较大的情况下,内存会迅速地消耗殆尽。
虽然有些乏味、不优雅,但之前提到的 bugs.php.net 链接中提供了一个解决方案。
这个方案在释放对象前使用一个 destructor 方法以达到目的。Destructor 方法可将所有内部的父对象引用全部清除,也就是说可以将这部分本来会溢出的内存释放掉。
以下是“修复后”的代码:
[/code]
注意那个新增的
为什么会有内存溢出的发生?我对PHP内核方面的研究并不精通,但可以确定的是此问题与引用计数有关系。
在 $bar 中引用 $foo 的引用计数不会因为父对象 $foo 被释放而递减,这时PHP认为你仍需要 $foo 对象,也就不会释放这部分的内存……大概是这样。
这里确实可以看出我的无知,但大体意思是:一个引用计数没有递减,所以一些内存永远得不到释放。
在前面提到的 bugs.php.net 链接中我看到修改垃圾回收的过程将会牺牲极大的性能,因为我对引用计数了解不多,所以我认为这是真的。
与其改变垃圾回收的过程,为什么不用 unset() 对内部对象做释放的工作呢?(或者在释放对象的时候调用 __destruct()?)
也许PHP内核开发者可以在此或其他地方,对这种垃圾回收处理机制做出修改。
当然,我们可以按自己的意愿调用 unset() 函数来释放内存,但通常不需要这么做。
不过在PHP里,至少有一种情况内存不会得到自动释放,即便是手动调用 unset().
问题症状
如果两个对象之间存在着相互引用的关系,如“父对象-子对象”,对父对象调用 unset() 不会释放在子对象中引用父对象的内存(即便父对象被垃圾回收,也不行)。
有些糊涂了?我们来看下面的这段代码:
<?php class 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()) . "/n"; } ?>
[/code]
运行这段代码,你会看到内存使用率越来越高越来越高,直到用光光。
... 33,551,616 33,551,976 33,552,336 33,552,696 PHP Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 16 bytes) in memleak.php on line 17
[/code]
对大部分PHP程序员来讲这种情况不算是什么问题。
可如果你在一个长期运行的代码中使用到了一大堆相互引用的对象,尤其是在对象相对较大的情况下,内存会迅速地消耗殆尽。
Userland解决方案
虽然有些乏味、不优雅,但之前提到的 bugs.php.net 链接中提供了一个解决方案。这个方案在释放对象前使用一个 destructor 方法以达到目的。Destructor 方法可将所有内部的父对象引用全部清除,也就是说可以将这部分本来会溢出的内存释放掉。
以下是“修复后”的代码:
<?php class 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()) . "/n"; } ?>
[/code]
注意那个新增的
Foo::__destruct()方法,以及在释放对象前对 $foo->__destruct() 的调用。现在这段代码解决了内存使用率一直增加的问题,这么一来,代码就可以很好的工作了。
PHP内核解决方案?
为什么会有内存溢出的发生?我对PHP内核方面的研究并不精通,但可以确定的是此问题与引用计数有关系。在 $bar 中引用 $foo 的引用计数不会因为父对象 $foo 被释放而递减,这时PHP认为你仍需要 $foo 对象,也就不会释放这部分的内存……大概是这样。
这里确实可以看出我的无知,但大体意思是:一个引用计数没有递减,所以一些内存永远得不到释放。
在前面提到的 bugs.php.net 链接中我看到修改垃圾回收的过程将会牺牲极大的性能,因为我对引用计数了解不多,所以我认为这是真的。
与其改变垃圾回收的过程,为什么不用 unset() 对内部对象做释放的工作呢?(或者在释放对象的时候调用 __destruct()?)
也许PHP内核开发者可以在此或其他地方,对这种垃圾回收处理机制做出修改。
相关文章推荐
- PHP对象相互引用的内存溢出实例分析
- PHP对象相互引用的内存溢出实例分析
- PHP 对象和引用总结
- php中引用&的真正理解-变量引用、函数引用、对象引用
- php中对象是引用类型吗?
- php中引用&的真正理解-变量引用、函数引用、对象引用
- PHP 数组和对象的相互转化
- php的变量引用,函数引用,对象引用
- php对象引用及序列化
- php值和对象的值传递和引用传递
- php中引用&的用法分析【变量引用,函数引用,对象引用】
- 【PHP内存泄漏案例】PHP对象递归引用造成内存泄漏
- php数组和对象相互转换
- 速战速决 (6) - PHP: 获取 http 请求数据, 获取 get 数据 和 post 数据, json 字符串与对象之间的相互转换
- php中的对象引用和复制
- php的引用&(就是在变量或者函数、对象等前面加上&符号)
- php中引用&的真正理解-变量引用、函数引用、对象引用
- PHP------数组和对象相互转化,stdClass Object转array
- PHP------数组和对象相互转化,stdClass Object转array
- 跟着百度学PHP[4]OOP面对对象编程-5-内部引用$this