php数组采用copy-on-write机制,赋值时不复制数据仅增引用计数,写操作时才分离复制;嵌套引用会禁用该机制,可通过debug_zval_dump观察refcount变化。

PHP 的数组在底层实现中采用 copy-on-write(写时复制)机制,这决定了数组变量赋值、函数传参、返回值等场景下内存是否真正复制。理解它,能帮你避免意外的性能开销和逻辑陷阱。
什么是 copy-on-write?
Copy-on-write 是一种优化策略:多个变量可以共享同一块内存中的数组数据,直到其中某个变量尝试修改数组内容时,PHP 才会真正复制一份独立副本供其使用。这意味着:
- 单纯的赋值(
$b = $a)不会立即复制数组数据,只增加引用计数 - 只要所有变量都只读访问,内存始终共用
- 一旦任一变量调用
array_push()、$arr[] = ...、unset()、sort()等写操作,就会触发复制
实际表现与常见误区
很多开发者误以为 $b = $a 后修改 $b 会影响 $a,或相反——其实不会,因为写操作自动触发分离:
$a = [1, 2, 3]; $b = $a; // 共享 zval,refcount=2 $b[] = 4; // 写操作 → 复制 $b 的数据,$a 不变 var_dump($a); // [1, 2, 3] var_dump($b); // [1, 2, 3, 4]
但要注意:如果数组内嵌套了引用(如通过 &$ 显式引用),COW 不再适用,行为会改变。
立即学习“PHP免费学习笔记(深入)”;
如何观察 COW 是否发生?
可通过 xdebug_debug_zval() 或 PHP 8.0+ 的 debug_zval_dump() 查看 zval 引用计数和是否为引用:
$a = [1, 2, 3];
xdebug_debug_zval('a'); // refcount=1, is_ref=0
$b = $a;
xdebug_debug_zval('a'); // refcount=2, is_ref=0 ← 说明共享未复制
$b[] = 4;
xdebug_debug_zval('a'); // refcount=1, is_ref=0 ← 已分离
注意:启用 OPcache 或某些扩展可能影响 refcount 显示,建议在 CLI 模式下测试。
对性能和编码的影响
COW 让大数组赋值几乎零开销,但也带来隐式成本:
- 函数参数默认按值传递,但大数组传入时不会立刻复制;若函数内部修改,则复制发生在函数内,而非调用点
- 返回大数组的函数(如
get_all_users())不会导致调用方额外复制,除非后续写入 - 想强制深拷贝(例如为避免后续意外修改),需显式调用
unserialize(serialize($arr))或递归 clone(PHP 8.3+ 支持ArrayObject::export()类似能力) - 频繁读写混合场景下,COW 可能引发多次复制,此时可考虑改用对象封装或 SplFixedArray 提升确定性
不复杂但容易忽略:COW 是 PHP 数组高效的基础,不是 bug,而是设计契约。写代码时只需记住——“赋值不复制,写才分离”。










