标签:des blog http io ar 使用 sp for on
PHP出现如下错误:Allowed memory size of xxx bytes exhausted at xxx:xxx (tried to allocate xxx bytes)
关于这一点,本站点中,http://nodonkey.iteye.com/blog/728223 有所讲述。
同时,还有 http://hi.baidu.com/thinkinginlamp/blog/item/e400f819a3caab7cdbb4bd4e.html 此文也是讲这个问题的。
为什么大家都讲了,这里还要讲,是不是多此一举?其实不是。这个问题的英文原文是在:http://paul-m-jones.com/archives/262
可以看出,有必要再次总结一下。
如果PHP对象存在递归引用,就会出现内存泄漏。这个Bug在PHP里已经存在很久很久了,先让我们来重现这个Bug:
- <?php
- class Foo {
- function __construct() {
- $this->bar = new Bar($this);
- }
- }
-
- class Bar {
- function __construct($foo) {
- $this->foo = $foo;
- }
- }
-
- for ($i = 0; $i < 100; $i++) {
- $obj = new Foo();
-
- unset($obj);
-
- echo memory_get_usage(), "\n";
- }
- ?>
运行以上代码,你会发现,内存使用量本应该不变才对,可实际上却是不断增加,unset没有完全生效。
我们看到,英文原文中给出的做法是,手工调用析构函数。为什么要这样做呢?
我们给出一段新的代码,注意一下,代码中的注释对问题的讲解:
- <?php
- class Foo {
- function __construct() {
-
-
-
- $this->bar = new Bar($this);
- }
- function __destruct() {
- echo(‘Foo die --3--<br>‘);
- unset($this->bar);
- }
-
- function destroy()
- {
- echo(‘destroy --1--<br>‘);
- unset($this->bar);
- }
- }
-
- class Bar {
- function __construct($foo) {
- $this->foo = $foo;
- }
-
- function __destruct() {
- echo(‘Bar die --2--<br>‘);
- }
- }
-
- for ($i = 0; $i < 100; $i++) {
- echo memory_get_usage(), "<br>";
- $obj = new Foo();
-
-
-
- $obj->destroy();
- unset($obj);
- }
- echo memory_get_usage(), "<br>";
- ?>
我们可以发现,当我们注解掉上面这段代码中的42行[$obj->destroy();]时,__destruct中的调用被PHP延迟了。即如果PHP对象存在递归引用,PHP并不及时释放内存。即使你使用unset,希望强制释放也没有用。
我们引用部分运行结果:
99616
99984
100352
100720
101088
Foo die --3--
Bar die --2--
Foo die --3--
Bar die --2--
Foo die --3--
Bar die --2--
Foo die --3--
Bar die --2--
Foo die --3--
Bar die --2--
可以看出Foo,Bar是在程序结束时死的,并且,Foo先死,Bar后死。这是因为,PHP明了,你们都不需要它们了。
一旦我们手工调用[$obj->destroy();],由于PHP对象存在递归引用已被破坏,所以,unset就起作用了。
我们引用部分运行结果:
64256
destroy --1--
Bar die --2--
Foo die --3--
65008
destroy --1--
Bar die --2--
Foo die --3--
65008
destroy --1--
Bar die --2--
Foo die --3--
可以看出,Foo,Bar是在你调用destroy后立即死亡,内存因而不再增长了。
此BUG据说是在php5.3中解决了。如果说是在php5.3中可以手工调用gc_collect_cycles这个函数。
假如是这样,其实与你在class中手工添加destroy让用户调用,没有什么区别。
递归引用会被经常用到。特别是用PHP构建树结构对象。注意英文原文中,用户评论中的链接:http://www.alexatnet.com/node/73
其中给出的代码:
- class Node {
- public $parentNode;
- public $childNodes = array();
- function Node() {
- $this->nodeValue = str_repeat(‘0123456789‘, 128);
- }
- }
- function createRelationship() {
- $parent = new Node();
- $child = new Node();
- $parent->childNodes[] = $child;
- $child->parentNode = $parent;
- }
- echo ‘Initial: ‘ . number_format(memory_get_usage(), 0, ‘.‘, ‘,‘) . " bytes\n";
- for($i = 0; $i < 10000; $i++) {
- createRelationship();
- }
- echo ‘Peak: ‘ . number_format(memory_get_peak_usage(), 0, ‘.‘, ‘,‘) . " bytes\n";
- echo ‘End: ‘ . number_format(memory_get_usage(), 0, ‘.‘, ‘,‘) . " bytes\n";
输出结果是:
Initial: 327,336 bytes
Peak: 35,824,176 bytes
End: 35,823,328 bytes
作者建议的方式就是:增加destroy方法,
- class Node {
- public $parentNode;
- public $childNodes = array();
- function Node() {
- $this->nodeValue = str_repeat(‘0123456789‘, 128);
- }
- function destroy()
- {
- $this->parentNode = null;
- $this->childNodes = array();
- }
- }
-
- function createRelationship() {
- $parent = new Node();
- $child = new Node();
- $parent->childNodes[] = $child;
- $child->parentNode = $parent;
- $parent->destroy();
- }
另外作者还给出了php5.3中的解决方法:使用php5.3中的新函数,手工调用gc_collect_cycles这个函数。
- function createRelationship() {
- $parent = new Node();
- $child = new Node();
- $parent->childNodes[] = $child;
- $child->parentNode = $parent;
- gc_collect_cycles();
- }
同时,作者也告诉了我们,调用gc_collect_cycles这个函数,我们不需要额外增加destroy了,但是性能上有一些缺陷。
我们还可以对代码作进一步改进。那就是改成静态函数。具体如下:
- <?php
- class Foo {
- function __construct() {
-
-
-
- $this->bar = new Bar($this);
- }
- function __destruct() {
- echo(‘Foo die --3--<br>‘);
- unset($this->bar);
- }
-
- static function destroy($obj)
- {
- echo(‘destroy --1--<br>‘);
- unset($obj->bar);
- unset($obj);
- }
- }
-
- class Bar {
- function __construct($foo) {
- $this->foo = $foo;
- }
-
- function __destruct() {
- echo(‘Bar die --2--<br>‘);
- }
- }
-
- for ($i = 0; $i < 100; $i++) {
- echo memory_get_usage(), "<br>";
- $obj = new Foo();
-
- Foo::destroy($obj);
-
- }
- echo memory_get_usage(), "<br>";
- ?>
PHP内存溢出 Allowed memory size of 解决办法
标签:des blog http io ar 使用 sp for on
原文地址:http://www.cnblogs.com/mako/p/4156982.html