background image

达到了用 5 个字段存储 8 种类型的值。
  而当前 zval 中的 value(value 的类型即是 _zvalue_value)到底表示那种类型,则由
“_zval_struct”中的 type 确定。_zval_struct 即是 zval 在 C 语言中的具体实现,每个 zval 表示
一 个 变 量 的 内 存 对 象 。 除 了 value 和 type , 可 以 看 到 _zval_struct 中 还 有 两 个 字 段
refcount__gc 和 is_ref__gc,从其后缀就可以断定这两个家伙与垃圾回收有关。没错,PHP
的垃圾回收全靠这俩字段了。其中 refcount__gc 表示当前有几个变量引用此 zval,而
is_ref__gc 表示当前 zval 是否被按引用引用,这话听起来很拗口,这和 PHP 中 zval 的
“Write-On-Copy”机制有关,由于这个话题不是本文重点,因此这里不再详述,读者只需
记住 refcount__gc 这个字段的作用即可。
  PHP5.2

——

中的垃圾回收算法

Reference Counting

  PHP5.2 中使用的内存回收算法是大名鼎鼎的 Reference Counting,这个算法中文翻译

叫做 引用计数 ,其思想非常直观和简洁:为每个内存对象分配一个计数器,当一个内
存对象建立时计数器初始化为 1(因此此时总是有一个变量引用此对象),以后每有一个新
变量引用此内存对象,则计数器加 1,而每当减少一个引用此内存对象的变量则计数器
减 1,当垃圾回收机制运作的时候,将所有计数器为 0 的内存对象销毁并回收其占用的内
存。而 PHP 中内存对象就是 zval,而计数器就是 refcount__gc。
  例如下面一段 PHP 代码演示了 PHP5.2 计数器的工作原理(计数器值通过 xdebug 得到):
 
  <?php
  $val1 = 100; //zval(val1).refcount_gc = 1;
    $val2 = $val1; //zval(val1).refcount_gc = 2,zval(val2).refcount_gc = 2( 因 为 是 Write on 
copy,当前 val2 与 val1 共同引用一个 zval)
  $val2 = 200; //zval(val1).refcount_gc = 1,zval(val2).refcount_gc = 1(此处 val2 新建了一个
zval)
  unset($val1); //zval(val1).refcount_gc = 0($val1 引用的 zval 再也不可用,会被 GC 回收)
  ?>
 
  Reference Counting 简单直观,实现方便,但却存在一个致命的缺陷,就是容易造成
内存泄露。很多朋友可能已经意识到了,如果存在循环引用,那么 Reference Counting 就
可能导致内存泄露。例如下面的代码:
 
  <?php
  $a = array();
  $a[] = & $a;
  unset($a);
  ?>
 
  这段代码首先建立了数组 a,然后让 a 的第一个元素按引用指向 a,这时 a 的 zval 的
refcount 就变为 2,然后我们销毁变量 a,此时 a 最初指向的 zval 的 refcount 为 1,但是我
们再也没有办法对其进行操作,因为其形成了一个循环自引用,如下图所示: