ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

php的垃圾回收机制

2020-11-26 17:35:18  阅读:238  来源: 互联网

标签:容器 变量 refcount 回收 zval 垃圾 php ref 引用


PHP垃圾回收机制

引用计数机制

每个php变量存在于一个zval变量容器中。一个zval变量容器, 在php5时,zval的定义如下:

struct _zval_struct {
     union {
          long lval;
          double dval;
          struct {
               char *val;
               int len;
          } str;
          HashTable *ht;
          zend_object_value obj;
          zend_ast *ast;
     } value;
     zend_uint refcount__gc;
     zend_uchar type;
     zend_uchar is_ref__gc;
};

zval变量容器,包含了变量的值和类型,还包括了is_ref,是个布尔值,标识这个变量是否属于引用变量,refcount表示指向zval变量容器的个数。

Example
当一个变量被赋值时,就会生成一个zval变量容器,如:

<?php
$a = 'new string'

那么在上述例子中, zval的变量信息是多少呢?

<?php
xdebug_debug_zval('a');

会显示输出:

a: (refcount=1, is_ref=0)='new string'

Example
若增加一个zval的引用计数

<?php
$a = "new string";
$b = $a;
xdebug_debug_zval( 'a' );

上述会显示:

a: (refcount=2, is_ref=0)='new string'

这时,引用次数是2, 因为同一变量容器被变量a和b关联,php不会复制已生成的变量容器。变量容器在refcount为0时销毁,任何关联到变量容器的变量,离开它的作用域(如函数执行结束)或调用unset()函数,refcount就会减1。

Example
减少引用计数

$a = "new string";
$c = $b = $a;
xdebug_debug_zval( 'a' );
unset( $b, $c );
xdebug_debug_zval( 'a' );

上述会显示:

a: (refcount=3, is_ref=0)='new string'
a: (refcount=1, is_ref=0)='new string'

复合类型

当变量是array或object这样的复合数据类型时,与标量类型的值不同,array和 object类型的变量把它们的成员或属性存在自己的符号表中。这意味着下面的例子将生成三个zval变量容器。

Example

$a = array( 'meaning' => 'life', 'number' => 42 );
xdebug_debug_zval( 'a' );

以上会输出:

a: (refcount=1, is_ref=0)=array (
   'meaning' => (refcount=1, is_ref=0)='life',
   'number' => (refcount=1, is_ref=0)=42
)

这三个zval变量容器是: ameaningnumber。增加和减少refcount的标量规则和上面提到的一样

Example
添加一个已经存在的元素到列表中

<?php
$a = array( 'meaning' => 'life', 'number' => 42 );
$a['life'] = $a['meaning'];
xdebug_debug_zval( 'a' );

上述会显示:

a: (refcount=1, is_ref=0)=array (
   'meaning' => (refcount=2, is_ref=0)='life',
   'number' => (refcount=1, is_ref=0)=42,
   'life' => (refcount=2, is_ref=0)='life'
)

Example
添加一个数组本身作为这个数组的元素

$a = array( 'one' );
$a[] =& $a;
xdebug_debug_zval( 'a' );

上述则会显示:

a: (refcount=2, is_ref=1)=array (
   0 => (refcount=1, is_ref=0)='one',
   1 => (refcount=2, is_ref=1)=...
)

能看到数组变量 (a) 同时也是这个数组的第二个元素(1) 指向的变量容器中“refcount”为 2。上面的输出结果中的"..."说明发生了递归操作, 显然在这种情况下意味着"..."指向原始数组。

跟刚刚一样,对一个变量调用unset,将删除这个符号,且它指向的变量容器中的引用次数也减1。所以,如果我们在执行完上面的代码后,对变量$a调用unset, 那么变量 $a 和数组元素 "1" 所指向的变量容器的引用次数减1, 从"2"变成"1"。

Example

$a = array( 'one' );
$a[] =& $a;
unset($a);
xdebug_debug_zval( 'a' );

上述则显示:

(refcount=1, is_ref=1)=array (
   0 => (refcount=1, is_ref=0)='one',
   1 => (refcount=1, is_ref=1)=...
)

清理变量容器的问题

尽管不再有某个作用域中的任何符号指向这个结构(就是变量容器),由于数组元素“1”仍然指向数组本身,所以这个容器不能被清除 。因为没有另外的符号指向它,用户没有办法清除这个结构,结果就会导致内存泄漏。

PHP回收周期

传统上,php用引用计数内存机制,无法处理循环引用内存泄漏。但是php5.3使用引用计数系统中同步周期回收的同步算法,来处理内存泄漏。

这个算法的基础规则是,如果一个变量引用计数增加,它将继续使用。如果变量的引用计数减少到0,那么变量容器将会被清除。也就是说,在引用计数减少到非零值时, 才会产生垃圾周期。在一个垃圾周期中,通过检查引用计数是否减1,并且检查哪些变量容器的引用次数是零,来发现哪部分是垃圾。

回收周期步骤如下:

  • 为了避免检查所有的引用计数可能减少的周期,这个算法把所有可能根,放在根缓冲区(用紫色标记,称为疑似垃圾),这样可以确保每个可能的垃圾根在缓冲区中只出现一次。仅仅在缓冲区满了时, 对缓冲区所有的不同变量容器执行垃圾回收操作。

  • 模拟删除每个紫色变量。模拟删除可能将将不是紫色的普通变量引用次数减1,如果普通变量的引用次数为0,就对这个普通变量在做一次模拟删除。每个变量只能被模拟删除一次,模拟删除后标记为灰色。

  • 模拟恢复每个紫色变量。恢复条件是,变量的引用计数大于0,才做模拟恢复,每个变量只能恢复一次,恢复后标记为黑色。

  • 这样剩下一些没有被恢复的就是该删除的蓝色节点了,然后将蓝色节点遍历出来,真正删除。

标签:容器,变量,refcount,回收,zval,垃圾,php,ref,引用
来源: https://www.cnblogs.com/simple-record/p/14043408.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有