php 的垃圾回收策略

2018-10-11 18:48:34   最后更新: 2018-10-11 18:48:34   访问数量:130




此前我们了解过 java 和 python 如何管理内存以及运行过程中的垃圾收集

java 编程思想(第四版)

python 的内存管理与垃圾收集

 

本篇日志我们来介绍博主另一个比较熟悉的语言 -- php 的内存管理方式以及垃圾回收机制

 

所有的 php 类型在 php 内部都是用一个 zval 结构存储的,下面是 zval 的存储结构 _zval_struct:

typedef union _zvalue_value { long lval; /* long value */ double dval; /* double value */ struct { char *val; int len; } str; HashTable *ht; /* hash table value */ zend_object_value obj; } zvalue_value; struct _zval_struct { zvalue_value value; zend_uint refcount__gc; zend_uchar type; /* active type */ zend_uchar is_ref__gc; };

 

 

其中联合体“_zvalue_value”用于表示PHP中所有变量的值,这里之所以使用union,是因为一个zval在一个时刻只能表示一种类型的变量

可以看到,_zval_struct 中除了 zvalue_value 类型的变量值和 zend_uchar 类型的变量类型数据以外,还存储了 refcount__gc 和 is_ref__gc 两个字段

  1. is_ref -- is_ref 是一个 bool 类型的值,他用来标识该变量是否属于引用集合(reference set)
  2. refcount -- 引用计数器

 

查看变量完整信息

通过 xdebug_debug_zval 函数可以查看变量的完整信息:

<?php $a = "new string"; xdebug_debug_zval('a'); ?> 打印出了: a: (refcount=1, is_ref=0)='new string'

 

 

与 python 一样,php 也是通过引用计数法来实现内存的回收的,变量中的 refcount 字段就是为了实现这一目的存在的

当一个对象的引用计数变为 0 时, 它被垃圾回收

当然,与 python 已经其他使用引用计数法作为垃圾回收机制的语言一样,PHP 的垃圾回收机制也存在循环引用问题

 

下面的例子说明了这一情况:

<?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)=...

)

 

 

在 php 执行过程中,循环引用的变量的引用计数永远不可能减到0,也就永远不会被引用计数规则的垃圾回收器回收,这样就造成了内存泄漏

 

PHP5.3.0 实现了回收周期算法,从而处理了因为循环引用造成的内存泄漏问题

可以通过在配置文件中指定 zend.enable_gc  来修改是否进行该算法的垃圾回收

 

这个算法的主要步骤如下:

  1. 算法建立了一个根缓冲区,所有的 zval 变量容器都存放在根缓冲区中,如下图紫色部分
  2. 当缓冲区满时,垃圾回收器遍历整个根缓冲区,将所有根缓冲区中的变量全部模拟删除
  3. 模拟恢复每个已经被模拟删除的变量,原则是只恢复模拟删除后引用计数大于 0 的变量
  4. 清除所有在模拟恢复步骤中没有被恢复的变量

 

由于在对象全部被模拟删除,循环引用的对象的引用计数会相应减少到 0,从而解决了循环引用造成的内存泄漏问题

 

http://php.net/manual/zh/features.gc.php

https://researcher.watson.ibm.com/researcher/files/us-bacon/Bacon01Concurrent.pdf

 






技术帖      php      技术分享      gc      垃圾回收      引用计数      回收周期      zval     


京ICP备15018585号