PHP的架构
从下图可以看出,php从下到上是一个4层体系:
前面有几篇文章提到,PHP中的变量都储存在’zval’这样一个容器中。在’zval’结构体中,存在is_ref和refcount两个元素分别来标志这个变量是否为引用集合(reference set)以及被引用的次数。我们很容易想到,既然refcount表示被引用的次数,那么当refcount=0的时候就表示没有变量指向这个zval容器,那么我们就可以将这个zval容器回收了。这就是引用计数垃圾回收机制。
但是,这样可能会发生循环引用的问题,会造成内存泄露。
创建一个数组类型变量$a,给变量$a新增一个元素,该元素为数组变量$a的一个引用。此时变量$a的refcount为2。
前几天在研究PHP引用变量的时候敲测试代码发现了一个很神奇的现象。在使用Xdebug扩展中的xdebug_debug_zval()函数查看变量的引用计数refcount的时候,发现PHP7和PHP5.4输出的结果竟然不一样,而且PHP7中输出的refcount竟然是0。具体测试代码和输出结果如下:
PHP5.6输出:
Version: 5.6.27
string(11) “Hello World” refcount(2)
a: (refcount=1, is_ref=0)=’Hello World’
PHP7.0输出:
Version: 7.0.12
string(11) “Hello World” refcount(1)
a: (refcount=0, is_ref=0)=’Hello World’
本文第一部分和第二均翻译自Nikita Popov(nikic,PHP 官方开发组成员,柏林科技大学的学生) 的博客。为了更符合汉语的阅读习惯,文中并不会逐字逐句的翻译。
要理解本文,你应该对 PHP5 中变量的实现有了一些了解,本文重点在于解释 PHP7 中 zval 的变化。
第一部分讲了 PHP5 和 PHP7 中关于变量最基础的实现和变化。这里再重复一下,主要的变化就是 zval 不再单独分配内存,不自己存储引用计数。整型浮点型等简单类型直接存储在 zval 中。复杂类型则通过指针指向一个独立的结构体。
复杂的 zval 数据值有一个共同的头,其结构由 zend_refcounted
定义:
|
|
这个头存储有 refcount
(引用计数),值的类型 type
和循环回收的相关信息 gc_info
以及类型标志位 flags
。
接下来会对每种复杂类型的实现单独进行分析并和 PHP5 的实现进行比较。引用虽然也属于复杂类型,但是上一部分已经介绍过了,这里就不再赘述。另外这里也不会讲到资源类型(因为作者觉得资源类型没什么好讲的)。
本文第一部分和第二均翻译自Nikita Popov(nikic,PHP 官方开发组成员,柏林科技大学的学生) 的博客。为了更符合汉语的阅读习惯,文中并不会逐字逐句的翻译。
要理解本文,你应该对 PHP5 中变量的实现有了一些了解,本文重点在于解释 PHP7 中 zval 的变化。
由于大量的细节描述,本文将会分成两个部分:第一部分主要描述 zval(zend value) 的实现在 PHP5 和 PHP7 中有何不同以及引用的实现。第二部分将会分析单独类型(strings、objects)的细节。
PHP5 中 zval 结构体定义如下:
|
|