- 每个值容器(zend_value)里面都会有引用计数结构体 zend_rrefcounted
- 引用计数减少到 0 时进行清除
- unset() 将值的引用减少 1
- 值容器引用在减少 1 后如果不为 0 ,则加入回收池
- 回收池满后,开始执行回收
- 第一次遍历回收池所有值容器。对所有值容器的引用减 1 。如果是嵌套对象(如数组和对象),则遍历其元素,元素值容器的引用减 1 。
- 这是一个深度遍历的过程。
- 这里是模拟减 1,减到 0 时不触发回收
- 第二次遍历回收池所有值容器。对引用计数不为 0 的值容器引用加 1 (恢复到之前的样子),并将其从回收池中移出。
- 回收池中引用计数为 0 的值容器被作为垃圾清除。
如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| <?php
$arr = [];
$arr[0] = 'gc';
xdebug_debug_zval('arr');
// 输出:
// arr: (refcount=1, is_ref=0)=array (0 => (interned, is_ref=0)='gc')
$arr[1] = &$arr;
xdebug_debug_zval('arr');
// 输出:
// arr: (refcount=2, is_ref=1)=array (0 => (interned, is_ref=0)='gc', 1 => (refcount=2, is_ref=1)=...)
$arr[2] = &$arr;
xdebug_debug_zval('arr');
// 输出:
// arr: (refcount=3, is_ref=1)=array (0 => (interned, is_ref=0)='gc', 1 => (refcount=3, is_ref=1)=..., 2 => (refcount=3, is_ref=1)=...)
$arr[3] = $arr;
xdebug_debug_zval('arr');
// 输出:
// arr: (refcount=5, is_ref=1)=array (0 => (interned, is_ref=0)='gc', 1 => (refcount=5, is_ref=1)=..., 2 => (refcount=5, is_ref=1)=..., 3 => (refcount=1, is_ref=0)=array (0 => (interned, is_ref=0)='gc', 1 => (refcount=5, is_ref=1)=..., 2 => (refcount=5, is_ref=1)=...))
unset($arr);
xdebug_debug_zval('arr');
// 输出:
// arr: no such symbol
|
unset 前,$arr 引用计数为 5 。
unset 将 $arr 引用计数减 1 ,得到 $arr 引用计数 4。
遍历 $arr 。
将 $arr[0] 值容器引用计数减 1,不影响 $arr 。
将 $arr[1] 值容器引用计数减 1,得到 $arr 引用计数 3 。
将 $arr[2] 值容器引用计数减 1,得到 $arr 引用计数 2 。
将 $arr[3] 值容器引用计数减 1。
遍历 $arr[3] 。
将 $arr[3][1] 引用计数减 1,得到 $arr 引用计数 1 。
将 $arr[3][2] 引用计数减 1,得到 $arr 引用计数 0 。
回收 $arr 。