php垃圾回收机制简介

参考 http://php.net/

php zval数据结构

struct _zval_struct {
    zvalue_value value;     /* value */
    zend_uint refcount__gc;  /* variable ref count */
    zend_uchar type;          /* active type */
    zend_uchar is_ref__gc;    /* if it is a ref variable */
};
typedef struct _zval_struct zval;
1.第一个是"is_ref",是个bool值,用来标识这个变量是否是属于引用集合(reference set)。
2.第二个额外字节是"refcount",用以表示指向这个zval变量容器的变量(也称符号即symbol)个数。

第一个例子,使用xdebug查看 zval变量容器的内部数据结构。
<?php
$a = "new string";
?>

<?php
xdebug_debug_zval('a');
?>
a: (refcount=1, is_ref=0)='new string'

第二个例子,
增加一个zval的引用计数,通过赋值观察refcount的变化
<?php
$a = "new string";
$b = $a;
xdebug_debug_zval( 'a' );
?>
a: (refcount=2, is_ref=0)='new string'
第三个例子,减少引用计数来观察zval数据结构内部的变化
<?php
$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'

复合数据类型
<?php
$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内部指针关系的例子:

<?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'
)
图:
接下来就是一个会在php5.3版本之前导致内存泄漏的一个例子
<?php
$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)=...
)
上面的输出结果中的"..."说明发生了递归操作, 显然在这种情况下意味着"..."指向原始数组
图:

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

php的同步算法处理引用内存泄漏的方法回头再更(暂时没有深入看)

回收周期

php会把可能根(疑似垃圾的跟)放在根缓存区里面(默认大小为10000),调用gc_enable() 和 gc_disable()函数来打开和关闭垃圾回收机制。

但是即使关闭了垃圾回收机制,可能根还是会存进根缓存区里面(相比每次检查回收机制是否打开,还不如存进去),但是当缓存区存满之后,如果还有疑似根就不会存进去了,可能造成内存泄漏。

因此 就在你调用gc_disable()函数释放内存之前,先调用gc_collect_cycles()函数可能比较明智。因为这将清除已存放在根缓冲区中的所有可能根,然后在垃圾回收机制被关闭时,可留下空缓冲区以有更多空间存储可能根。

垃圾回收会带来时间成本的增加,约百分之10(不到)。但是内存溢出会有明显抑制(特别是在大量多个对象互相指向的时候)

在php5.3以上版本,如果发现一个zval容器中的refcount在减少,并没有减到0,PHP会把该值放到缓冲区,当做有可能是垃圾的可疑对象。

具体参看PHP官网 http://php.net/manual/zh/features.gc.performance-considerations.php



 
原文地址:https://www.cnblogs.com/tobemaster/p/8180091.html