php写时复制
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
时,会存储双精度浮点数。
PHP5 中的引用计数
在PHP5中,zval 的内存是单独从堆(heap)中分配的(有少数例外情况),PHP 需要知道哪些 zval 是正在使用的,哪些是需要释放的。所以这就需要用到引用计数:zval 中 refcount__gc
的值用于保存 zval 本身被引用的次数,比如 $a = $b = 42
语句中,42
被两个变量引用,所以它的引用计数就是 2。如果引用计数变成 0,就意味着这个变量已经没有用了,内存也就可以释放了。
注意这里提及到的引用计数指的不是 PHP 代码中的引用(使用 &
),而是变量的使用次数。后面两者需要同时出现时会使用『PHP 引用』和『引用』来区分两个概念,这里先忽略掉 PHP 的部分。
一个和引用计数紧密相关的概念是『写时复制』:对于多个引用来说,zaval 只有在没有变化的情况下才是共享的,一旦其中一个引用改变 zval 的值,就需要复制(”separated”)一份 zval,然后修改复制后的 zval。
下面是一个关于『写时复制』和 zval 的销毁的例子
<?php $a = 42; // $a -> zval_1(type=IS_LONG, value=42, refcount=1) $b = $a; // $a, $b -> zval_1(type=IS_LONG, value=42, refcount=2) $c = $b; // $a, $b, $c -> zval_1(type=IS_LONG, value=42, refcount=3) // 下面几行是关于 zval 分离的 $a += 1; // $b, $c -> zval_1(type=IS_LONG, value=42, refcount=2) // $a -> zval_2(type=IS_LONG, value=43, refcount=1) unset($b); // $c -> zval_1(type=IS_LONG, value=42, refcount=1) // $a -> zval_2(type=IS_LONG, value=43, refcount=1) unset($c); // zval_1 is destroyed, because refcount=0 // $a -> zval_2(type=IS_LONG, value=43, refcount=1)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异