Reference Counting 简单直观,实现方便,但却存在一个致命的缺陷,就是容易造
成内存泄露。很多朋友可能已经意识到了,如果存在循环引用,那么
Reference
Counting 就可能导致内存泄露。例如下面的代码:
1
2
3
4
5
<?php
$a
= array();
$a[] = & $a;
unset($a);
?>
这段代码首先建立了数组
a,然后让 a 的第一个元素按引用指向 a,这时 a 的 zval 的
refcount 就变为 2,然后我们销毁变量 a,此时 a 最初指向的 zval 的 refcount 为
1,但是我们再也没有办法对其进行操作,因为其形成了一个循环自引用,如下图所示:
其中灰色部分表示已经不复存在。由于
a 之前指向的 zval 的 refcount 为 1(被其
HashTable 的第一个元素引用),这个 zval 就不会被 GC 销毁,这部分内存就泄露了。
这里特别要指出的是,
PHP 是通过符号表(Symbol Table)存储变量符号的,全局
有一个符号表,而每个复杂类型如数组或对象有自己的符号表,因此上面代码中,
a
和
a[0]是两个符号,但是 a 储存在全局符号表中,而 a[0]储存在数组本身的符号表中,
且这里
a 和 a[0]引用同一个 zval(当然符号 a 后来被销毁了)。希望读者朋友注意分
清符号(
Symbol)的 zval 的关系。
在
PHP 只用于做动态页面脚本时,这种泄露也许不是很要紧,因为动态页面脚本的生
命周期很短,
PHP 会保证当脚本执行完毕后,释放其所有资源。但是 PHP 发展到目前
已经不仅仅用作动态页面脚本这么简单,如果将
PHP 用在生命周期较长的场景中,例
如自动化测试脚本或
deamon 进程,那么经过多次循环后积累下来的内存泄露可能就