PHP垃圾回收机制详解
发布:smiling 来源: PHP粉丝网 添加日期:2014-02-28 13:42:38 浏览: 评论:0
PHP 可以自动进行内存管理,清除不再需要的对象.PHP使用了引用计数(reference counting)这种单纯的垃圾回收(garbage collection)机制.每个对象都内含一个引用计数器,每个reference连接到对象,计数器加1.当reference离开生存空间或被设为 NULL,计数器减1.当某个对象的引用计数器为零时,PHP知道你将不再需要使用这个对象,释放其所占的内存空间.
php 5.3之前使用的垃圾回收机制是单纯的“引用计数”,也就是每个内存对象都分配一个计数器,当内存对象被变量引用时,计数器+1;当变量引用撤掉后,计数器-1;当计数器=0时,表明内存对象没有被使用,该内存对象则进行销毁,垃圾回收完成.
“引用计数”存在问题,就是当两个或多个对象互相引用形成环状后,内存对象的计数器则不会消减为0;这时候,这一组内存对象已经没用了,但是不能回收,从而导致内存泄露;php5.3开始,使用了新的垃圾回收机制,在引用计数基础上,实现了一种复杂的算法,来检测内存对象中引用环的存在,以避免内存泄露.
php变量存在一个叫"zval"的变量容器中,"zval"变量容器包括含变量的类型和值,还包括额外的两个字节信息,分别是“is_ref”表示变量是否属于引用,“refcount”指向这个zval变量容器的变量个数.
如果你安装了xdebug,就可以用xdebug_debug_zval()显示“zval”的信息了.如下:
- $str = "www.111cn.net";
- xdebug_debug_zval('str');
结果:
str:(refcount=1, is_ref=0),
string 'phpddt.com' (length=10)
只有当变量容器在”refcount“变成0时就被销毁.当你unset()一个变量时,想要的“zval”中refcount就会减1,再来说说前几天遇到的unset引用问题:
实例代码如下:
- $a = "aaa";
- $b = & $a;
- unset($a);
- //echo $b; //这里依然会输出aaa,用xdebug_debug_zval打印你就知道为什么了
- xdebug_debug_zval("b");
结果:
- b:(refcount=1, is_ref=0),string 'aaa' (length=3)
实例代码如下:
- $arr = array( 'a' => 'aaa', 'b' => "bbb" );
- xdebug_debug_zval( 'arr' );
- $arr['aaa'] = $arr['a'];
- xdebug_debug_zval( 'arr' );
- ?>
结果:
- arr:
- (refcount=1, is_ref=0),
- array
- 'a' => (refcount=1, is_ref=0),string 'aaa' (length=3)
- 'b' => (refcount=1, is_ref=0),string 'bbb' (length=3)
- arr:
- (refcount=1, is_ref=0),
- array
- 'a' => (refcount=2, is_ref=0),string 'aaa' (length=3)
- 'b' => (refcount=1, is_ref=0),string 'bbb' (length=3)
- 'aaa' => (refcount=2, is_ref=0),string 'aaa' (length=3)
可以看到看到原有的数组元素和新添加的数组元素关联到同一个"refcount"2的zval变量容器.这里我也只是起到抛砖引玉的作用.
上面我们只是简单的使用了unset,null,mysql_close,__destruct,xdebug_debug_zval 接着往下看,查看内存是否泄露,看是否有该释放的内存没有被释放,可以简单的通过 调用 memory_get_usage 函数查看内存使用情况来判断;memory_get_usage 函数返回的内存使用数据据说不是很准确,可以使用 php 的 xdebug 扩展来获得更准确翔实的内存使用情况.
实例代码如下:
- class A{
- private $b;
- function __construct(){
- $this->b = new B($this);
- }
- function __destruct(){
- //echo "A destructn";
- }
- }
- class B{
- private $a;
- function __construct($a){
- $this->a = $a;
- }
- function __destruct(){
- //echo "B descturctn";
- }
- }
- for($i=0;;$i++){
- $a = new A();
- if($i%1000 == 0){
- echo memory_get_usage()."n";
- }
- }
- }
上面就构造了一个会产生环状引用的例子;每次创建一个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,环就被打破了,内存泄露也就不会发生了.
3.其他:
1)垃圾回收的时机
Php中,引用计数为0,则内存立刻释放;也就是说,不存在环状引用的变量,离开变量的作用域,内存被立刻释放.
环状引用检测则是在满足一定条件下触发,所以在上面的例子中,会看到使用的内存有大幅度的波动;也可以通过 gc_collect_cycles 函数来主动进行环状引用检测.
2) &符号的影响
显式引用一个变量,会增加该内存的引用计数:
此时unset($a), 但是仍有$b指向该内存区域的引用,内存不会释放.
3)unset函数的影响
unset只是断开一个变量到一块内存区域的连接,同时将该内存区域的引用计数-1;在上面的例子中,循环体内部,$a=new A(); unset($a);并不会将$a的引用计数减到零;
4)= null 操作的影响;
$a = null 是直接将$a 指向的数据结构置空,同时将其引用计数归0.
5)脚本执行结束的影响
Tags: 垃圾 回收 机制
- 上一篇:PHP MVC框架 视图之常用学习笔记
- 下一篇:PHP判断浏览器类型程序代码详解
相关文章
- ·简单谈谈PHP的垃圾回收机制(2021-09-05)
- ·PHP进阶学习之垃圾回收机制详解(2021-11-26)
- ·PHP中变量引用与变量销毁机制详解(2015-12-10)
- ·PHP 7错误处理机制介绍(2018-09-22)
- ·PHP 7错误处理机制介绍(2018-09-28)
- ·PHP使用反射机制实现查找类和方法的所在位置(2019-08-22)
- ·Fpm启动机制及流程的详细分析(附代码)(2020-01-15)
- ·PHP学习之信号处理机制的操作(2020-02-27)
- ·PHP OPP机制和模式简介(抽象类、接口和契约式编程)(2021-02-09)
- ·PHP中的插件机制原理和实例(2021-03-14)
- ·PHP缓存机制Output Control详解(2021-03-20)
- ·php中socket通信机制实例详解(2021-05-05)
- ·基于PHP实现的事件机制实例分析(2021-05-28)
- ·PHP实现事件机制的方法(2021-06-11)
推荐文章
热门文章
最新评论文章
- 写给考虑创业的年轻程序员(10)
- PHP新手上路(一)(7)
- 惹恼程序员的十件事(5)
- PHP邮件发送例子,已测试成功(5)
- 致初学者:PHP比ASP优秀的七个理由(4)
- PHP会被淘汰吗?(4)
- PHP新手上路(四)(4)
- 如何去学习PHP?(2)
- 简单入门级php分页代码(2)
- php中邮箱email 电话等格式的验证(2)