php 8.1大数组赋值采用写时复制(cow):赋值瞬间零内存增长、refcount+1;首次写入触发全量复制,内存近翻倍;引用赋值全程零复制但双向绑定;深拷贝代价高,应慎用。

PHP 8.1 中大数组赋值($b = $a)不立即复制数据,内存几乎不增长;只有首次写入时才触发底层哈希表分离,内存用量瞬间翻倍。这是写时复制(Copy-on-Write, COW)机制的典型表现,不是“复制”或“不复制”的二选一,而是“延迟复制”。
赋值瞬间:零新增内存,仅 refcount +1
执行 $b = $a 后,PHP 不分配新内存块,只是让 $b 和 $a 共享同一个 zval 和底层哈希表结构,zval 的 refcount 加 1。此时 memory_get_usage(true) 测得的内存几乎不变。
- 适用于含百万级元素的数值/字符串数组,无论多大,只要不改就不多占内存
- foreach 遍历、array_keys()、count() 等只读操作均不触发复制
- 可用
xdebug_debug_zval('a')查看 refcount 和 is_ref 标志确认共享状态
首次写入:COW 触发,内存突增约 100%
一旦对任一副本执行写操作(如 $b[0] = 123、$b[] = 'new'、unset($b['key'])),PHP 立即为该变量创建完整副本,包括所有键值对和哈希桶结构。此时内存占用会接近翻倍。
- 实测:100 万整数数组(约 34 MB)赋值后修改一个元素,内存升至约 66 MB
- 复制是全量的,不是按需分块;嵌套子数组也会被一并复制
- 即使只改一个元素,整个数组结构仍需重建,无法跳过未修改部分
引用赋值(&):全程零复制,但完全绑定
若用 $b =& $a,则两者始终指向同一内存地址,无任何复制行为。修改 $a 或 $b 都实时反映在对方身上,且内存占用恒定。
- 适合需要原地更新、批量处理、递归构建等场景
- 函数参数也同理:
function process(&$arr)可避免大数组传参时意外触发 COW - 注意副作用:调用方必须接受原始数组可能被修改的事实
深拷贝的代价与替代方案
显式深拷贝(如 unserialize(serialize($a)) 或递归 clone)会强制立即分配双份内存,且 serialize/unserialize 还有类型丢失、性能差、不支持资源/闭包等限制。
- 大数组慎用深拷贝;如需快照,优先考虑不可变语义 + 写时复制封装
- 对超大结构(如 GB 级),可结合 memory-mapped 文件或生成器流式处理,绕过内存加载瓶颈
- 调试时建议用
memory_get_usage(true)在关键操作前后采样,而非依赖总内存变化
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











