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:

  1. <?php
  2. class Foo {
  3. function __construct() {
  4. $this->bar = new Bar($this);
  5. }
  6. }
  7. class Bar {
  8. function __construct($foo) {
  9. $this->foo = $foo;
  10. }
  11. }
  12. for ($i = 0; $i < 100; $i++) {
  13. $obj = new Foo();
  14. unset($obj);
  15. echo memory_get_usage(), "\n";
  16. }
  17. ?>

运行以上代码,你会发现,内存使用量本应该不变才对,可实际上却是不断增加,unset没有完全生效。
    我们看到,英文原文中给出的做法是,手工调用析构函数。为什么要这样做呢?
    我们给出一段新的代码,注意一下,代码中的注释对问题的讲解:

  1. <?php
  2. class Foo {
  3. function __construct() {
  4. //PHP5.3以前的版本,它是一个长生不老药。因为创建了它,引用了自己。
  5. //实际上,只要外部有任一个对象的属性中引用了这个类,这个类和引用它的类都不能被unset
  6. //这就是我们在这里犯下了错。
  7. $this->bar = new Bar($this);
  8. }
  9. function __destruct() {
  10. echo('Foo die --3--<br>');
  11. unset($this->bar);
  12. }
  13. /**
  14. * 因为我们在这里犯下了错,我们要在此收拾残局。所以,添加一个收尸函数
  15. * 这一做法,也就是不要在外面调用__destruct,看上去很不雅观。
  16. * 但增加此函数有一大好处。就是向使用者表明,需要我们主动把它杀掉。而不是等它自动死掉。
  17. * 如果不愿意,你大可以直接在外部调用__destruct
  18. */
  19. function destroy()
  20. {
  21. echo('destroy --1--<br>');
  22. unset($this->bar);
  23. }
  24. }
  25. class Bar {
  26. function __construct($foo) {
  27. $this->foo = $foo;
  28. }
  29. function __destruct() {
  30. echo('Bar die --2--<br>');
  31. }
  32. }
  33. for ($i = 0; $i < 100; $i++) {
  34. echo memory_get_usage(), "<br>";
  35. $obj = new Foo();
  36. //注解掉,或放开注解下面这一行,运行一下。结果分别在本文后面。
  37. //我们可以发现,如果我们不收尸,那么,PHP主动调用__destruct,则是在程序结束时才执行。
  38. //如果我们主动做了,就能及时释放内存。
  39. $obj->destroy();
  40. unset($obj);
  41. }
  42. echo memory_get_usage(), "<br>";
  43. ?>

我们可以发现,当我们注解掉上面这段代码中的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
    其中给出的代码:

  1. class Node {
  2. public $parentNode;
  3. public $childNodes = array();
  4. function Node() {
  5. $this->nodeValue = str_repeat('0123456789', 128);
  6. }
  7. }
  8. function createRelationship() {
  9. $parent = new Node();
  10. $child = new Node();
  11. $parent->childNodes[] = $child;
  12. $child->parentNode = $parent;
  13. }
  14. //And then let's review the amount of memory allocated by this script after 10,000 calls to createRelationship function.
  15. echo 'Initial: ' . number_format(memory_get_usage(), 0, '.', ',') . " bytes\n";
  16. for($i = 0; $i < 10000; $i++) {
  17. createRelationship();
  18. }
  19. echo 'Peak: ' . number_format(memory_get_peak_usage(), 0, '.', ',') . " bytes\n";
  20. 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方法,

  1. class Node {
  2. public $parentNode;
  3. public $childNodes = array();
  4. function Node() {
  5. $this->nodeValue = str_repeat('0123456789', 128);
  6. }
  7. function destroy()
  8. {
  9. $this->parentNode = null;
  10. $this->childNodes = array();
  11. }
  12. }
  13. function createRelationship() {
  14. $parent = new Node();
  15. $child = new Node();
  16. $parent->childNodes[] = $child;
  17. $child->parentNode = $parent;
  18. $parent->destroy();
  19. }

另外作者还给出了php5.3中的解决方法:使用php5.3中的新函数,手工调用gc_collect_cycles这个函数。

  1. function createRelationship() {
  2. $parent = new Node();
  3. $child = new Node();
  4. $parent->childNodes[] = $child;
  5. $child->parentNode = $parent;
  6. gc_collect_cycles();
  7. }

同时,作者也告诉了我们,调用gc_collect_cycles这个函数,我们不需要额外增加destroy了,但是性能上有一些缺陷。

我们还可以对代码作进一步改进。那就是改成静态函数。具体如下:

    1. <?php
    2. class Foo {
    3. function __construct() {
    4. //PHP5.3以前的版本,它是一个长生不老药。因为创建了它,引用了自己。
    5. //实际上,只要外部有任一个对象的属性中引用了这个类,这个类和引用它的类都不能被unset
    6. //这就是我们在这里犯下了错。
    7. $this->bar = new Bar($this);
    8. }
    9. function __destruct() {
    10. echo('Foo die --3--<br>');
    11. unset($this->bar);
    12. }
    13. /**
    14. * 因为我们在这里犯下了错,我们要在此收拾残局。所以,添加一个收尸函数
    15. * 这一做法,也就是不要在外面调用__destruct,看上去很不雅观。
    16. * 但增加此函数有一大好处。就是向使用者表明,需要我们主动把它杀掉。而不是等它自动死掉。
    17. * 如果不愿意,你大可以直接在外部调用__destruct
    18. * 实际上,这样做以后,也就不用清理内存时,写两行代码,一行代码就够了。
    19. */
    20. static function destroy($obj)
    21. {
    22. echo('destroy --1--<br>');
    23. unset($obj->bar);
    24. unset($obj);
    25. }
    26. }
    27. class Bar {
    28. function __construct($foo) {
    29. $this->foo = $foo;
    30. }
    31. function __destruct() {
    32. echo('Bar die --2--<br>');
    33. }
    34. }
    35. for ($i = 0; $i < 100; $i++) {
    36. echo memory_get_usage(), "<br>";
    37. $obj = new Foo();
    38. //改用静态函数后,这里只要一行。
    39. Foo::destroy($obj);
    40. }
    41. echo memory_get_usage(), "<br>";
    42. ?>

PHP内存溢出 Allowed memory size of 解决办法的更多相关文章

  1. PHP内存溢出Allowed memory size of 解决办法

    PHP内存溢出Allowed memory size of 解决办法 博客分类: php   ============================Allowed memory size of  x ...

  2. (转载)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 ...

  3. 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 ...

  4. Allowed memory size of 134217728 bytes exhausted解决办法(php内存耗尽报错)【简记】

    报错: PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 72 bytes) i ...

  5. 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 从数据库 ...

  6. Fatal error: Allowed memory size of 8388608 bytes exhausted

    这两天安装bugfree,更换了一个数据量较大的库,结果打开bug详情页要么是空白页,要么就报如题的错误,错误信息还包括C:\wamp\www\bugfree\Include\Class\ADOLit ...

  7. 一次apk打开时报内存溢出错误,故写下内存溢出的各种原因和解决方法

    原转载:https://blog.csdn.net/cp_panda_5/article/details/79613870 正文内容: 对于JVM的内存写过的文章已经有点多了,而且有点烂了,不过说那么 ...

  8. PHPExcel 报 Allowed memory size of 8388608 byte

    使用 phpExcel 报 Allowed memory size of 8388608 bytes exhausted 错误,原因是php页面消耗的最大内存默认是为 8M (在PHP的ini件里可以 ...

  9. Allowed memory size of 134217728 bytes exhausted

    错误信息: Allowed memory size of 134217728 bytes exhausted (tried to allocate 65015808 bytes) 由于报错信息和数据库 ...

随机推荐

  1. maven执行"mvn clean package" 命令报错

    昨天利用mvn打包,执行程序'mvn clean package' 命令,发现打包失败 问题描述 具体看代码 发信tomcat下的log 清除不掉.为什么呢?忽然想起来我的项目服务还起着,于是我把服务 ...

  2. 使用flex布局调换两个按钮的位置

    组件用的时antd的Modal组件,里面的按钮需要调换一下位置 今天发现用flex布局非常方便,代码如下: display: flex; justify-content: center; flex-f ...

  3. Springboot 和 Mybatis集成开发

    Springboot 和 Mybatis集成开发 本项目使用的环境: 开发工具:Intellij IDEA 2017.1.3 jdk:1.7.0_79 maven:3.3.9 额外功能 PageHel ...

  4. C# QR二维码DEMO

    QR二维码 二维码的一种 相关类库 ThoughtWorks.QRCode 第三方类库 DEMO功能 Encode 生成二维码图片 Encoding 编码 Correction Level 等级 Ve ...

  5. MachineLearning Exercise 7 : K-means Clustering and Principle Component Analysis

    findClosestCentroids.m m = size(X,); :m [value index] = min(sum((repmat(X(i,:),K,)-centroids).^,)); ...

  6. LINQ 模糊搜索

    IList<entity> ls = new List<entity>(); ls = (from k in ls where k.Name.Contains("sa ...

  7. Win7剪贴板粘贴汉字显示为乱码的解决办法

    http://blog.csdn.net/tanaya/article/details/8684805 最近2天发现在记事本粘贴的时候汉字都显示为乱码了,很纠结,后面发现是[区域和语言]设置中的“文本 ...

  8. xml的相关知识

  9. JMH 使用指南 - java 性能测试

    JMH 篇 JMH,即Java Microbenchmark Harness 翻译:java 微基准测试 工具套件.## 1.添加依赖```<dependency> <groupId ...

  10. 解题:USACO15JAN Grass Cownoisseur

    解题 首先缩点没啥可说的,然后考虑枚举这次逆行的边.具体来说在正常的图和反图上各跑一次最长路,然后注意减掉起点的贡献,用拓扑排序实现(我这里瞎写了个Bellman_Ford,其实在DAG上这好像和拓扑 ...