php数组赋值后仅在refcount>1且执行写操作时触发拷贝,包括修改、追加、删除、排序及引用遍历后的写操作;读操作如遍历、取值、isset、count等不触发拷贝。

PHP 数组赋值后,只有在发生“写操作”且原 zval 的引用计数(refcount)大于 1 时,才会真正触发内存拷贝。这不是发生在赋值那一刻,也不是每次读取或遍历的时候,而是一个延迟、按需的分离过程。
哪些操作会立即触发拷贝?
只要满足两个条件:当前数组 zval 的 refcount > 1(即有多个变量指向它),且执行了具有“写语义”的操作。典型包括:
- 修改任意键的值:
$b['key'] = 'new'或$b[0] = 42 - 追加元素:
$b[] = 'x'、array_push($b, 'y') - 删除元素:
unset($b['key'])或unset($b[0]) - 重排顺序:
sort($b)、ksort($b)等原地排序函数 - 使用引用遍历时的后续写操作:
foreach ($b as &$v) { }后再改$b任何部分,会强制分离
哪些操作不会触发拷贝?
这些属于纯读操作,即使 refcount > 1,也完全共享底层数据,零额外内存开销:
- 遍历:
foreach ($b as $k => $v)(不带 &) - 读取值:
echo $b['key']、$x = $b[0] - 判断存在:
isset($b['key'])、empty($b) - 获取长度:
count($b) - 序列化只读:
json_encode($b)
一个关键细节:数组内部指针也会影响行为
如果原数组指针已处于非法状态(如调用过 end() + next()),那么第一次对副本执行 current()、key() 或任何会访问指针的操作,也可能触发分离——因为 PHP 需要为新数组初始化指针位置。这属于 COW 在数组结构层面的延伸表现。
如何验证是否发生了拷贝?
可用 xdebug_debug_zval('var_name') 查看:
-
refcount:反映共享程度,赋值后通常从 1 变为 2 -
is_ref:为 0 表示未显式引用;若为 1,则始终共享,不走 COW - 配合
memory_get_usage()在操作前后对比,能观察到突增点
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











