PHP的内存释放问题

参考http://www.laruence.com/2011/03/04/1894.html。php


初步总结:html

1.PHP会本身有一个内存管理,它会一次性向系统多申请一些内存,而后本身分配。node

2.PHP也是有计数器的概念,unset只是让计数器进行减一,不表明这块内存真的被释放了,只有当指向这块内存的引用数为0时,这块内存才获得释放,这个释放是还给PHP的内存管理。算法


如下是引用:http://www.nowamagic.net/librarys/veda/detail/1377。数组

PHP的内存泄露问题与垃圾回收

你写了一个php脚本,通常都不用考虑内存泄露和垃圾回收的问题,由于通常状况下你的脚本很快就执行完退出了。数据结构

但在一些运行时间长,数据量大的时候,程序运行一段时间后,php脚本就占用了过多内存,而后就报错(PHP Fatal error: Allowed memory size of 134217728 bytes exhausted)退出了。通常来讲,每一个页面处理结束,新建的simple_html_dom对象就应该被销毁了——可是实际上没有,很明显,内存泄露发生了。dom

PHP的垃圾回收机制

php 5.3以前使用的垃圾回收机制是单纯的“引用计数”,也就是每一个内存对象都分配一个计数器,当内存对象被变量引用时,计数器+1;当变量引用撤掉后,计数器-1;当计数器=0时,代表内存对象没有被使用,该内存对象则进行销毁,垃圾回收完成。函数

“引用计数”存在问题,就是当两个或多个对象互相引用造成环状后,内存对象的计数器则不会消减为0;这时候,这一组内存对象已经没用了,可是不能回收,从而致使内存泄露。工具

php5.3开始,使用了新的垃圾回收机制,在引用计数基础上,实现了一种复杂的算法,来检测内存对象中引用环的存在,以免内存泄露。this

查看内存是否泄露

看是否有该释放的内存没有被释放,能够简单的经过 调用  memory_get_usage  函数查看内存使用状况来判断;memory_get_usage 函数返回的内存使用数据听说不是很准确,能够使用  php 的 xdebug 扩展来得到更准确翔实的内存使用状况。

01 class A{
02     private $b;
03     function __construct(){
04         $this->b = new B($this);
05     }
06     function __destruct(){
07         //echo "A destruct\n";
08     }
09 }
10

11 class B{
12     private $a;
13     function __construct($a){
14         $this->a = $a;
15     }
16     function __destruct(){
17         //echo "B descturct\n";
18     }
19 }
20

21 for($i=0;;$i++){
22     $a new A();
23     if($i00 == 0){
24         echo memory_get_usage()."\n";
25     }
26 }

上面就构造了一个会产生环状引用的例子。每次建立一个A对象的实例a,a就建立一个B对象的实例b,同时让b引用a。这样,每一个A对象永远被一个B引用,而每一个B对象同时被一个对象A引用,引用环就这样产生了。

在php5.2的环境下执行这段代码,会发现内存使用在单调上涨,也没有A和B的析构函数被执行后输出的“A/B desctruct”信息;直到内存耗尽,输出“PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 40 bytes)”。

在php5.3的环境下执行这段代码,则发现内存使用在上跳下窜,可是永远没有超过一个限额。程序也会输出大量的“A/B desctruct”,这说明析构函数被调用了。

个人同事的程序中,就存在这种引用的环路,而他的脚本,实在php5.2.3下执行的。simple_html_dom工具中,有两个类,分别是simple_html_dom和simple_html_dom_node,前者中有一个数组成员变量nodes,数组中每一个元素都是一个simple_html_dom_node对象;而每一个simple_html_dom_node对象都有一个成员变量dom,该dom的值就是前面的simple_html_dom对象——这样就造成了一个漂亮的引用环,致使了内存泄露。解决的办法也很简单,就是simple_html_dom对象在使用完毕时,主动调用其clear函数,清空其成员变量nodes,环就被打破了,内存泄露也就不会发生了。

其余

1. 垃圾回收的时机

PHP中,引用计数为0,则内存马上释放。也就是说,不存在环状引用的变量,离开变量的做用域,内存被马上释放。环状引用检测则是在知足必定条件下触发,因此在上面的例子中,会看到使用的内存有大幅度的波动。也能够经过 gc_collect_cycles 函数来主动进行环状引用检测。

2. &符号的影响

显式引用一个变量,会增长该内存的引用计数:

1 $a "something";
2 $b = &$a;

此时unset($a), 可是仍有$b指向该内存区域的引用,内存不会释放。

3. unset函数的影响

unset只是断开一个变量到一块内存区域的链接,同时将该内存区域的引用计数-1;在上面的例子中,循环体内部,$a=new A(); unset($a);并不会将$a的引用计数减到零;

4. = null 操做的影响;

$a = null 是直接将$a 指向的数据结构置空,同时将其引用计数归0。

5. 脚本执行结束的影响

脚本执行结束,该脚本中使用的全部内存都会被释放,不管是否有引用环。