PHP内存溢出 Allowed memory size of 解决办法
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 解决办法的更多相关文章
- PHP内存溢出Allowed memory size of 解决办法
PHP内存溢出Allowed memory size of 解决办法 博客分类: php ============================Allowed memory size of x ...
- (转载)PHP的内存限制 Allowed memory size of 134217728 bytes exhausted (tried to allocate 1099 bytes) in
(转载)http://blog.csdn.net/beyondlpf/article/details/7794028 Fatal error: Allowed memory size of 13421 ...
- PHP的内存限制 Allowed memory size of 134217728 bytes exhausted (tried to allocate 1099 bytes) in
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 1099 bytes) in Fa ...
- Allowed memory size of 134217728 bytes exhausted解决办法(php内存耗尽报错)【简记】
报错: PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 72 bytes) i ...
- Fatal error: Allowed memory size of 524288000 bytes exhausted (tried to allocate 64 bytes) in D
Fatal error: Allowed memory size of 524288000 bytes exhausted (tried to allocate 64 bytes) in D 从数据库 ...
- Fatal error: Allowed memory size of 8388608 bytes exhausted
这两天安装bugfree,更换了一个数据量较大的库,结果打开bug详情页要么是空白页,要么就报如题的错误,错误信息还包括C:\wamp\www\bugfree\Include\Class\ADOLit ...
- 一次apk打开时报内存溢出错误,故写下内存溢出的各种原因和解决方法
原转载:https://blog.csdn.net/cp_panda_5/article/details/79613870 正文内容: 对于JVM的内存写过的文章已经有点多了,而且有点烂了,不过说那么 ...
- PHPExcel 报 Allowed memory size of 8388608 byte
使用 phpExcel 报 Allowed memory size of 8388608 bytes exhausted 错误,原因是php页面消耗的最大内存默认是为 8M (在PHP的ini件里可以 ...
- Allowed memory size of 134217728 bytes exhausted
错误信息: Allowed memory size of 134217728 bytes exhausted (tried to allocate 65015808 bytes) 由于报错信息和数据库 ...
随机推荐
- Sametime SDK
1,Sametime Server A.Sametime includes many server applications, which collectively provide the capab ...
- 【BZOJ2281】【Sdoi2011】黑白棋 解题报告
[BZOJ2281][Sdoi2011]黑白棋 Description 小A和小B又想到了一个新的游戏. 这个游戏是在一个\(1\)*\(n\)的棋盘上进行的,棋盘上有\(k\)个棋子,一半是 ...
- 单点登录(三)-----实战-----cas server 源码下载和部署
我们在上一篇文章中使用的是4.0版本的cas,4.0版本的有发布好的war包可以直接使用,那如果我们要使用更新的版本怎么办呢? 就需要下载源码自己编辑打包了. 步骤如下: 版本选择 我们在cas的gi ...
- Java虚拟机加载类
Java虚拟机是如何加载Java类的 Java虚拟机加载Java类总共需要经过3步:加载-----链接-----初始化.Java语言的类型可以分为两大类:基本类型和引用类型.基本类型是有Java虚拟机 ...
- java线程池赏析
1.线程池的顶级接口(Executor) 线程池的顶级接口(jdk > 1.5).仅仅定义了方法execute(Runnable). 该方法接收一个Runnable实例,用来执行一个任务,该任务 ...
- Andorid APK反逆向
Andorid APK反逆向解决方案---梆梆加固原理探寻http://blog.csdn.net/androidsecurity/article/details/8892635 Android AP ...
- 解题:CQOI 2013 和谐矩阵
题面 踩踩时间复杂度不正确的高斯消元 首先可以发现第一行确定后就可以确定整个矩阵,所以可以枚举第一行的所有状态然后$O(n)$递推检查是否合法 $O(n)$递推的方法是这样的:设$pre$为上一行,$ ...
- MySQL 第七篇:视图、触发器、事务、存储过程、函数
一 视图 视图是一个虚拟表(非真实存在),其本质是[根据SQL语句获取动态的数据集,并为其命名],用户使用时只需使用[名称]即可获取结果集,可以将该结果集当做表来使用. 使用视图我们可以把查询过程中的 ...
- 用ladon框架封装Python为Webservice接口以及调用接口的方法
一.用ladon框架封装Python为Webservice接口 功能实现的同时,希望将接口开放给别人,而封装python接口的一个再简单不过的框架Ladon,而且提供不同的协议,包括SOAP和Json ...
- UESTC--1548
题目:Easy math 原题链接:http://acm.uestc.edu.cn/problem.php?pid=1548 分析:费马小定理的应用. #include<cstdio> # ...