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() {
//PHP5.3以前的版本,它是一个长生不老药。因为创建了它,引用了自己。
//实际上,只要外部有任一个对象的属性中引用了这个类,这个类和引用它的类都不能被unset
//这就是我们在这里犯下了错。
$this->bar = new Bar($this);
}
function __destruct() {
echo('Foo die --3--<br>');
unset($this->bar);
}
/**
* 因为我们在这里犯下了错,我们要在此收拾残局。所以,添加一个收尸函数
* 这一做法,也就是不要在外面调用__destruct,看上去很不雅观。
* 但增加此函数有一大好处。就是向使用者表明,需要我们主动把它杀掉。而不是等它自动死掉。
* 如果不愿意,你大可以直接在外部调用__destruct
*/
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();
//注解掉,或放开注解下面这一行,运行一下。结果分别在本文后面。
//我们可以发现,如果我们不收尸,那么,PHP主动调用__destruct,则是在程序结束时才执行。
//如果我们主动做了,就能及时释放内存。
$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;
}
//And then let's review the amount of memory allocated by this script after 10,000 calls to createRelationship function.
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() {
//PHP5.3以前的版本,它是一个长生不老药。因为创建了它,引用了自己。
//实际上,只要外部有任一个对象的属性中引用了这个类,这个类和引用它的类都不能被unset
//这就是我们在这里犯下了错。
$this->bar = new Bar($this);
}
function __destruct() {
echo('Foo die --3--<br>');
unset($this->bar);
}
/**
* 因为我们在这里犯下了错,我们要在此收拾残局。所以,添加一个收尸函数
* 这一做法,也就是不要在外面调用__destruct,看上去很不雅观。
* 但增加此函数有一大好处。就是向使用者表明,需要我们主动把它杀掉。而不是等它自动死掉。
* 如果不愿意,你大可以直接在外部调用__destruct
* 实际上,这样做以后,也就不用清理内存时,写两行代码,一行代码就够了。
*/
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 134217728 bytes exhausted",這個錯誤是指 PHP腳本在執行過程中,超出了允許的内存限制,導致腳本無法繼續執行。在這篇文章中,我們將詳細解釋這個錯誤的原因、...
通过以上这些策略,你可以有效地解决PHP中"Allowed memory size of bytes exhausted"的问题,并优化你的代码以更高效地利用内存资源。记住,优化不仅关乎提高内存限制,更重要的是编写更聪明的代码,减少不必要的...
如果你的ThinkPHP提示你:致命错误(Fatal error: Allowed memory size),根据网上说的提高服务器可使用内存,我觉得都不是好的解决办法。麻烦也没必要。因为这是ThinkPHP本身存在BUG。 错误提示:Fatal error: ...
PHP错误“Allowed memory size of 67108864 bytes exhausted”通常表示程序运行过程中已用尽了分配给它的内存,导致内存溢出。这个错误在处理大数据量、复杂运算或者大型文件时尤其常见。在PHP中,默认的内存限制...
在PHP编程过程中,有时会遇到一个常见的错误提示:“Fatal error: Allowed memory size of [内存大小] bytes exhausted”,这意味着PHP在执行脚本时超过了分配给它的内存限制。这通常是由于程序存在内存泄漏、处理...
主要介绍了Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 2611816 bytes)错误的解决方法,需要的朋友可以参考下
根据提供的文件信息,我们将详细探讨在安装悟空CRM时遇到的“Fatal error: Allowed memory size of 8388608 bytes”错误及其解决办法。该问题通常表示PHP在执行过程中超过了分配的内存限制,导致程序无法继续运行。...
### Spring MVC 重定向导致内存溢出解决方案 在Spring MVC框架中进行页面重定向操作时,可能会遇到因重定向导致的内存溢出问题。本文将详细介绍该问题的原因、原理以及解决方法。 #### 一、问题背景及原因分析 在...
错误提示可能类似于“PHP Fatal error: Allowed memory size of XXX bytes exhausted”。针对这类问题,开发者可以采取多种策略来优化内存使用。 首先,要理解PHP的内存管理机制。PHP脚本默认有内存使用上限,可以...
这种情况通常表现为在处理一定数量的数据后,系统提示内存耗尽,例如“Allowed memory size of 134217728 bytes exhausted”。在描述中提到的现象是在对`edu_ocr_img`表进行归档操作时,每处理几万个数据,就会触发...
在运行PHP程序,通常会遇到“Fatal Error: Allowed memory size of xxxxxx bytes exhausted”的错误, 这个意味着PHP脚本使用了过多的内存,并超出了系统对其设置的允许最大内存
本文将详细介绍在使用curl访问特定域名时遇到405 Method Not Allowed错误的解决方法。首先,我们先来理解什么是405错误以及其背后可能的原因。 HTTP状态码405 Method Not Allowed表示客户端请求的HTTP方法不被...
然而,这种方式在处理大数据量时会消耗大量内存,从而可能导致"Allowed memory size exhausted"的错误。 为了解决这个问题,可以采用非缓冲查询(Unbuffered queries),也称为流式查询(Streaming queries)。非...
Laravel作为流行的PHP web开发框架,通过其灵活的架构和丰富的生态系统,使得开发者能够轻松地与各种数据库系统进行交互,包括Informix。 首先,我们需要了解Laravel的数据库连接和查询构建器。Laravel提供了统一的...
Android WebView 报 Not allowed to load local resource错误的解决办法 博客地址:http://blog.csdn.net/yuzhiqiang_1993/article/details/76228541
Allowed memory size of 8388608 bytes exhausted (tried to allocate 1298358 bytes) 出现该错误的原因:在确保不是程序产生的原因(例如死循环),是由于php页面消耗的最大内存默认是为 8M (在PHP的ini件里可以...