PHP垃圾回收机制

PHP5.2版本的垃圾回收机制主要针对zval结构中的refcount=0时释放变量,但复合类型的数组或对象,环形引用或者引用自己,都是造成内存泄露,PHP官网的介绍5.3的GC机制,通过将zval放入root buffer,进行模拟删除(refcount-1),当refcount==0时被认为是垃圾,而回收,下面是4个步骤:
GC

例如:

<?php
$a = array('one');
$a[] = &$a;
?>

这样$a数组就有两个元素,一个索引为0,值为字符one,另外一个索引为1,为$a自身的引用,内部存储如下:
a: (refcount=2, is_ref=1)=array (
0 => (refcount=1, is_ref=0)='one',
1 => (refcount=2, is_ref=1)=...
)
“...”表示1指向a自身,是一个环形引用:
t

按照网上表达所说:先放入root buffer中,refcount-1,这时$a的refcount=1,然后unset一下,refcount=0,则认为$a为垃圾
问题:
1. 我现在假设$a数组中,有2个key引用了自己,比如:

<?php
$a = array('one');
$a[] = &$a;
$a[] = &$a;
?>

a: (refcount=3, is_ref=1)=array (
0 => (refcount=1, is_ref=0)='one',
1 => (refcount=3, is_ref=1)=...
2 => (refcount=3, is_ref=1)=...
)
那么该数组进行unset后肯定是个垃圾,那么比如我先放到buffer, refcount-1,那么refcount=2,然后再unset-1,refcount=1,这样似乎虽然这个数组删除时会造成垃圾,但根据新的GC的机制说,只有当refcount=0时才会被认为是垃圾,那不是矛盾了吗? 那我数组里面是不是只要>=2的key引用了本身,都不会视为垃圾了?

阅读 6.3k
3 个回答

仔细看图你没发现问题么?明明产生了一个自引用,但是为什么refcount还是1呢?应该是变量释放到buffer里面然后refcount--的原因。你就是这块没理解对。

找到答案了吗?我尝试回答下

按照楼主说的,看看下面的内容:

$a = 123;
$b = $a;   //a: (refcount=2, is_ref=0)=123   b: (refcount=2, is_ref=0)=123
unset($b);    //a: (refcount=1, is_ref=0)=123

此时a所指向的zval也会进入垃圾周期,然后对refcount减一,是不是符号a指向的zval也要删除了?显然不是

这里根据我所了解的说说我的看法:GC不会对可能的垃圾根元素进行减一操作,而是会对根元素下面的元素进行减一操作,这样解释才合理。
就说说你这种情况,根节点zval计数不会进行减一操作,循环引用的时候,对循环引用指向的zval计数进行减一,这样不管多少循环引用都可以减到0,这样是不是可以说的通了。

虽然此问题都是 2014年的问题了,但现在还是适用的,故此来回答一波。

看来楼主对垃圾回收池处理的理解还不是太明白
1、只要zval.valuerefcount减一,然后缺其refcount的值不为0那么它就可能是垃圾,进入垃圾周期。
2、进入垃圾池遍历所有成员,包括其嵌套的成员,都对其做 refcount-1的操作,看外部的引用是否为0。

那么对于 题主的问题来说,
首先,你要想$a为垃圾,一定要先对 unset($a)操作,那么此时 $a的 refcount = 2,尤其减操作之后 其引用次数大于0,所以可能是垃圾啊,进入垃圾回收周期。
对于$a[0] refcount-1 不影响外部的$a
$a[1] refcount-1 ,此时 $arefount=1
$a[2] refcount-1 ,此时 $arefount=0
模拟减结束,那么此变量被当成垃圾销毁。

1 篇内容引用
推荐问题
宣传栏