background image

PHP 进阶:PHP 垃圾回收机制对内存泄露的处理

以下是关于 PHP 垃圾回收机制对内存泄露的处理的说明介绍,供大家参考下
上次说到了 refcount 和 is_ref,这里来说说内存泄露的情况
代码如下:
 

$a

 = 

array

(1, 2, &

$a

);

unset(

$a

);

 
在老的 PHP 版本中,这里就会出现内存泄露,分析如下:
执行第一行,可以知道

$a

$a

[2]指向的 zval refcount=2,is_ref=1

然后执行第二行,

$a

将会从符号表中被删除,同时指向的 zval 的 refcount--,此时

refcount=1,因为 refcount!=0,故此 zval 不会被当做垃圾回收,但是此时我们却失去了

$a

[2]

指向这个 zval 的入口,因此这个 zval 成了一块内存垃圾
同样的道理可以发生在类内部引用里,例如

 

复制代码 代码如下:
 

$a

 = 

new

 Man();

$a

->self = &

$a

;

unset(

$a

);

 
那么如何解决这种问题呢,新的 GC 机制采用了一个算法来解决这个问题
PHP 有一个 root buffer 用来存储 zval 的节点信息,当 root buffer 满了或者手动调用 gc 函数
时,GC 算法启动
对于一个数组或者类类型的 zval 而言,在垃圾回收机制启动时,算法会对该 zval 的数组/
类内部的元素/成员的 zval 进行一次遍历并将 refcount 减 1,如果说遍历完成后该 zval 的
refcount 被减为 0,则说明这个 zval 是一个内存垃圾,他将被销毁,见下面的例子

 

复制代码 代码如下:
 

$a

 = 

array

(1, 2, &

$a

, &

$a

);

unset(

$a

);

 
容易知道

$a

指向的 zval,假设为 z1 的 refcount=3,is_ref=1

当 unset(

$a

)执行的时候,

$a

就已经从符号表中删去,同时我们也失去了访问 z1 的入口,

此时 z1 refcount=2,is_ref=1
当 GC 启动时,会对该 z1 的数组元素的 zval 的 refcount 进行遍历减 1,遍历到 a[2]时,z1 
refcount--

 

, a[3]  

时 z1 refcount--,此时 z1 refcount = 0,即可将 z1 标记为内存垃圾,算法后

将其回收
总结来说可以这么表述:若一个数组类型的 zval,对他的元素 zval 进行一次遍历,同时