(点击上方公众号,可快速关注我们)
英文:Nikita Popov
中文:Scholer(@Scholer-L)
链接:http://0x1.im/blog/php/Internal-value-representation-in-PHP-7-part-1.html
本文第一部分和第二均翻译自Nikita Popov(nikic,PHP 官方开发组成员,柏林科技大学的学生) 的博客。为了更符合汉语的阅读习惯,文中并不会逐字逐句的翻译。
要理解本文,你应该对 PHP5 中变量的实现有了一些了解,本文重点在于解释 PHP7 中 zval 的变化。
由于大量的细节描述,本文将会分成两个部分:第一部分主要描述 zval(zend value) 的实现在 PHP5 和 PHP7 中有何不同以及引用的实现。第二部分将会分析单独类型(strings、objects)的细节。
PHP5 中的 zval
PHP5 中 zval 结构体定义如下:
typedef struct _zval_struct {
zvalue_value value;
zend_uint refcount__gc;
zend_uchar type;
zend_uchar is_ref__gc;
} zval;
如上,zval 包含一个 value、一个 type 以及两个 __gc 后缀的字段。value 是个联合体,用于存储不同类型的值:
typedef union _zvalue_value {
long lval; // 用于 bool 类型、整型和资源类型
double dval; // 用于浮点类型
struct { // 用于字符串
char *val;
int len;
} str;
HashTable *ht; // 用于数组
zend_object_value obj; // 用于对象
zend_ast *ast; // 用于常量表达式(PHP5.6 才有)
} zvalue_value;
C 语言联合体的特征是一次只有一个成员是有效的并且分配的内存与需要内存最多的成员匹配(也要考虑内存对齐)。所有成员都存储在内存的同一个位置,根据需要存储不同的值。当你需要 lval 的时候,它存储的是有符号整形,需要 dval 时,会存储双精度浮点数。
需要指出的是是联合体中当前存储的数据类型会记录到 type 字段,用一个整型来标记:
#define IS_NULL 0 /* Doesn't use value */
#define IS_LONG 1 /* Uses lval */
#define IS_DOUBLE 2 /* Uses dval */
#define IS_BOOL 3 /* Uses lval with values 0 and 1 */
#define IS_ARRAY 4 /* Uses ht */
#define IS_OBJECT 5 /* Uses obj */
#define IS_STRING 6 /* Uses str */
#define IS_RESOURCE 7 /* Uses lval, which is the resource ID */
/* Special types used for late-binding of constants */
#define IS_CONSTANT 8
#define IS_CONSTANT_AST 9
PHP5 中的引用计数
在PHP5中,zval 的内存是单独从堆(heap)中分配的(有少数例外情况),PHP 需要知道哪些 zval 是正在使用的,哪些是需要释放的。所以这就需要用到引用计数:zval 中 refcount__gc 的值用于保存 zval 本身被引用的次数,比如 $a = $b = 42 语句中,42 被两个变量引用,所以它的引用计数就是 2。如果引用计数变成 0,就意味着这个变量已经没有用了,内存也就可以释放了。
注意这里提及到的引用计数指的不是 PHP 代码中的引用(使用
Tag标签: