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运行原理有所帮助。

相关文章

  • Ajax+PHP实现的分类列表框功能示例

    Ajax+PHP实现的分类列表框功能示例

    这篇文章主要介绍了Ajax+PHP实现的分类列表框功能,涉及php结合ajax交互查询金沙国际官网与列表框构建相关操作技巧,需要的朋友可以参考下
    2019-02-02
  • str_replace只替换一次字符串的方法

    str_replace只替换一次字符串的方法

    我们都知道,在PHP里Strtr,strreplace等函数都可以用来替换,不过他们每次替换的时候都是全部替换,但是如果你想只替换一个或两个怎么办呢?看下边的解决方法
    2013-04-04
  • 掌握PHP垃圾回收机制详解

    掌握PHP垃圾回收机制详解

    这篇文章主要介绍了PHP垃圾回收机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • Linux编译升级php的详细方法

    Linux编译升级php的详细方法

    Linux编译升级php的详细方法,有配置php.ini、配置php-fpm.conf的代码。
    2013-11-11
  • php外部执行命令函数用法小结

    php外部执行命令函数用法小结

    这篇文章主要介绍了php外部执行命令函数用法,结合实例形式分析了exec与system执行外部命令的相关使用技巧,需要的朋友可以参考下
    2016-10-10
  • PHP 编程安全性小结

    PHP 编程安全性小结

    PHP 编程安全性小结,这4个小技巧,大家可以参考一些成熟的cms程序。如dedecms等
    2010-01-01
  • PHP中删除变量时unset()和null的区别分析

    PHP中删除变量时unset()和null的区别分析

    当程序里不再使用某些大体积的变量时(如:数组或对象),我们有必要删除它们
    2011-01-01
  • PHP 小心urldecode引发的SQL注入漏洞

    PHP 小心urldecode引发的SQL注入漏洞

    在我初学 PHP 的时候,看的是学校图书馆的一本烂书,里面根本就没写 PHP 在处理表单的时候会自动 urldecode,所以自己用 urldecode 函数来解码
    2011-10-10
  • PHP中HTML标签过滤技巧

    PHP中HTML标签过滤技巧

    htmlspecialchars()函数的过滤效果始终不如strip_tags()函数的过滤效果好,下面有个不错的方法,感兴趣的朋友可以参考下,希望对大家有所帮助
    2014-01-01
  • php判断字符以及字符串的包含方法属性

    php判断字符以及字符串的包含方法属性

    php判断字符以及字符串的包含,可以使用PHP的内置函数strstr,strpos,stristr直接进行判断.也可以通过explode函数的作用写一个判断函数
    2008-08-08

最新评论