PHP 8.2.10 的 zval 是16字节类型调度中枢,含8字节 value 联合体、type/type_flags/extra 位域及多功能 u2 字段,引用计数下沉至 zend_string 等结构,实现内存紧凑与运行时灵活统一。

在 PHP 8.2.10 源码中,zval 结构已高度优化,整体大小固定为 16 字节(x86_64 架构下),其设计兼顾内存紧凑性与运行时灵活性。它不再像 PHP 5 那样直接暴露 refcount__gc 和 is_ref__gc 字段,而是将类型、引用计数、是否为引用等元信息通过位域和联合体巧妙整合。
zval 的核心组成(PHP 8.2.10 实际定义)
源码位于 Zend/zend_types.h,精简后的逻辑结构如下:
struct _zval_struct {
zend_value value; /* 联合体,8 字节:存 lval/dval/str/arr/obj 等 */
union {
struct {
uint8_t type; /* 当前活跃类型,如 IS_STRING、IS_LONG */
uint8_t type_flags; /* 类型属性标志(如是否可复制、是否可调用) */
uint16_t extra; /* 用途多样:如对象 handle、数组 hash 表掩码、常量 flags */
} v;
uint32_t type_info; /* type + type_flags + extra 合并为一个 uint32_t */
} u1;
union {
uint32_t next; /* 哈希表冲突链指针 */
uint32_t cache_slot; /* OPcache 相关缓存槽位 */
uint32_t lineno; /* AST 行号(编译期) */
uint32_t num_args; /* 函数调用参数个数 */
uint32_t fe_pos; /* foreach 迭代位置 */
uint32_t fe_iter_idx; /* foreach 迭代器索引 */
uint32_t access_flags; /* 属性/方法访问控制 */
uint32_t property_guard;/* 只读属性保护标记 */
uint32_t extra; /* 通用扩展字段 */
} u2;
};注:
zend_value是一个 8 字节联合体(union _zend_value),包含lval(整型)、dval(浮点)、str(zend_string*)、arr(zend_array*)、obj(zend_object*)、res(zend_resource*)、ref(zend_reference*)等成员,具体使用哪个取决于type字段。
关键字段说明
-
value
真实数据载体。不是拷贝值,而是指向堆内存的指针(字符串、数组、对象等)或直接内联存储(小整数、布尔、null)。例如:-
IS_LONG→value.lval直接存int64_t -
IS_STRING→value.str指向zend_string结构(含len、h、val[])
-
-
u1.v.type
主类型标识符,定义在Zend/zend_types.h中,常见值包括:#define IS_NULL 0 #define IS_FALSE 1 #define IS_TRUE 2 #define IS_LONG 3 #define IS_DOUBLE 4 #define IS_STRING 5 #define IS_ARRAY 6 #define IS_OBJECT 7 #define IS_RESOURCE 8 #define IS_REFERENCE 9 // 引用变量(&$a) #define IS_IMMUTABLE 10 // PHP 8.2+ 只读类/属性专用标记
-
u1.v.type_flags
描述该类型的运行时行为,比如:-
ZEND_TYPE_REFCOUNTED:表示该类型需参与引用计数(如字符串、数组) -
ZEND_TYPE_COPYABLE:支持写时复制(COW) -
ZEND_TYPE_COLLECTABLE:可能触发 GC(如循环引用的对象)
-
引用计数不再直接暴露在 zval 中
PHP 7+ 将 refcount 移入zend_refcounted头部(如zend_string、zend_array自带gc.refcount__gc字段)。zval本身只通过type_flags和value.counted指针间接关联。u2是多功能复用字段
不同上下文语义不同:在哈希表中是next指针;在 OPcache 中是cache_slot;在foreach中记录迭代状态。这减少了结构体膨胀,提升缓存局部性。
实际内存布局(小端序,x86_64)
| 偏移 | 字节数 | 字段 | 说明 |
|---|---|---|---|
| 0 | 8 | value |
数据联合体 |
| 8 | 1 | u1.v.type |
类型(IS_STRING 等) |
| 9 | 1 | u1.v.type_flags |
类型行为标志 |
| 10–11 | 2 | u1.v.extra |
扩展用途(如对象 handle) |
| 12–15 | 4 | u2.next |
复用字段(依场景而变) |
为什么这样设计?
- 节省内存:16 字节对齐,适配 CPU 缓存行,减少 L1 cache miss
- 提速访问:常用字段(type/value)前置,利于指令预取
-
解耦管理:refcount/GC 逻辑下沉到具体结构(
zend_string等),zval更轻量 -
支持新特性:
IS_IMMUTABLE类型标记为只读类、枚举值等提供底层支撑
PHP 8.2.10 的 zval 已不是“变量容器”,而是类型调度中枢 + 内存视图代理——它不保存数据,只告诉引擎“去哪里找”和“该怎么用”。
立即学习“PHP免费学习笔记(深入)”;



















