PHP对象相互引用的内存溢出实例分析

 更新时间:2014年08月28日 10:20:45   投稿:shichen2014  
这篇文章主要介绍了PHP对象相互引用的内存溢出实例分析,是PHP5.3以下版本中常见的一个bug,文中给出了解决方法,需要的朋友可以参考下

通常来说使用脚本语言最大的好处之一就是可利用其拥有的自动垃圾回收机制来释放内存。你不需要在使用完变量后做任何释放内存的处理,因为这些PHP会帮你完成。
当然,我们可以按自己的意愿调用 unset() 函数来释放内存,但通常不需要这么做。
不过在PHP里,至少有一种情况内存不会得到自动释放,即便是手动调用 unset()。详情可考PHP官网关于内存泄露的分析:http://bugs.php.net/bug.php?id=33595

问题症状如下:

如果两个对象之间存在着相互引用的关系,如“父对象-子对象”,对父对象调用 unset()不会释放在子对象中引用父对象的内存(即便父对象被垃圾回收,也不行)。

是不是有些糊涂了?我们来看下面的这段代码:

<?
phpclass 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()) . " ";
}
?>

运行这段代码,你会看到内存使用率越来越高越来越高,直到用光光。

...33,551,61633,551,97633,552,33633,552,696PHP Fatal error: Allowed memory size of 33554432 bytes exhausted(tried to allocate 16 bytes) in memleak.php on line 17

对大部分PHP程序员来讲这种情况不算是什么问题。可如果你在一个长期运行的代码中使用到了一大堆相互引用的对象,尤其是在对象相对较大的情况下,内存会迅速地消耗殆尽。

Userland解决方案

虽然有些乏味、不优雅,但之前提到的 bugs.php.net 链接中提供了一个解决方案。
这个方案在释放对象前使用一个 destructor 方法以达到目的。Destructor 方法可将所有内部的父对象引用全部清除,也就是说可以将这部分本来会溢出的内存释放掉。

以下是“修复后”的代码:

<?
phpclass 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()) . " ";
}
?>

注意那个新增的Foo::__destruct()方法,以及在释放对象前对 $foo->__destruct() 的调用。现在这段代码解决了内存使用率一直增加的问题,这么一来,代码就可以很好的工作了。

PHP内核解决方案

为什么会有内存溢出的发生?我对PHP内核方面的研究并不精通,但可以确定的是此问题与引用计数有关系。
在 $bar 中引用 $foo 的引用计数不会因为父对象 $foo 被释放而递减,这时PHP认为你仍需要 $foo 对象,也就不会释放这部分的内存。原理大致如此。

通俗的来说,大体意思是:一个引用计数没有递减,所以一些内存永远得不到释放。
此外在前面提到的 bugs.php.net 链接中指出了修改垃圾回收的过程将会牺牲极大的性能,需要读者对此注意。

与其改变垃圾回收的过程,为什么不用 unset() 对内部对象做释放的工作呢?(或者在释放对象的时候调用 __destruct()?)
也许PHP内核开发者可以在此或其他地方,对这种垃圾回收处理机制做出修改。

相信本文所述对大家深入理解PHP运行原理有所帮助。

相关文章

  • php 采集书并合成txt格式的实现代码

    php 采集书并合成txt格式的实现代码

    记得上次有过一个叫采集后的处理这个就是它的升级版本 连采再处理,合成一本书txt的。
    2009-03-03
  • php中isset与empty函数的困惑与用法分析

    php中isset与empty函数的困惑与用法分析

    这篇文章主要介绍了php中isset与empty函数的困惑与用法,结合实例形式分析了php中isset与empty函数的功能、用法、区别及相关使用注意事项,需要的朋友可以参考下
    2019-07-07
  • PHP操作XML中XPath的应用示例

    PHP操作XML中XPath的应用示例

    这篇文章主要介绍了PHP操作XML中XPath的应用,结合实例形式分析了php使用XPath进行xml节点查询的相关操作技巧,需要的朋友可以参考下
    2019-07-07
  • 详解PHP数组赋值方法

    详解PHP数组赋值方法

    这篇文章主要介绍了详解PHP数组赋值方法,文章就怎样创建数组、怎样给PHP数组赋值,文章都做了详细的介绍和讲解,希望对大家有帮助。
    2015-11-11
  • PHPWind与Discuz截取字符函数substrs与cutstr性能比较

    PHPWind与Discuz截取字符函数substrs与cutstr性能比较

    PHP性能优化系列第四期实战篇,如何使用PEAR Benchmark类工具获取函数执行性能,这里以PHPWind与Discuz!两个社区论坛的截取字符函数substrs与cutstr为例,分析两个函数的执行性能情况
    2011-12-12
  • PHP中使用正则表达式提取中文实现笔记

    PHP中使用正则表达式提取中文实现笔记

    这篇文章主要介绍了PHP中使用正则表达式提取中文实现笔记,本文还讲解了韩文、日文的正则表达式,并同时给出实现代码和使用示例,需要的朋友可以参考下
    2015-01-01
  • php 8小时时间差的解决方法小结

    php 8小时时间差的解决方法小结

    前几天发现一个问题 date("Y-m-d G:i T",time()) 取出来的时间和本地时间老差8个小时,可我的系统明明是中国标准时(GMT+8),最后终于找到了问题所在。
    2009-12-12
  • php版微信返回用户text输入的方法

    php版微信返回用户text输入的方法

    这篇文章主要介绍了php版微信返回用户text输入的方法,结合实例形式分析了php实现的微信解析及返回用户输入数据的相关操作技巧,需要的朋友可以参考下
    2016-11-11
  • PHP正则匹配到2个字符串之间的内容方法

    PHP正则匹配到2个字符串之间的内容方法

    今天小编就为大家分享一篇PHP正则匹配到2个字符串之间的内容方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-12-12
  • PHP实现基于图的深度优先遍历输出1,2,3...n的全排列功能

    PHP实现基于图的深度优先遍历输出1,2,3...n的全排列功能

    这篇文章主要介绍了PHP实现基于图的深度优先遍历输出1,2,3...n的全排列功能,涉及php数据结构中图的遍历及全排列相关数学运算技巧,需要的朋友可以参考下
    2017-11-11

最新评论