php数组赋值默认采用写时复制(cow),$a和$b共享底层存储直至任一被修改才复制;引用赋值(&)则全程零复制、完全绑定;深拷贝需递归实现,serialize/unserialize性能差且有局限。

PHP 中数组复制不是简单的指针引用,而是根据上下文触发深拷贝或浅拷贝行为,但实际机制比“深/浅”二分更精细——它依赖于数组是否被修改(写时复制,Copy-on-Write)以及是否包含引用类型。
默认赋值是写时复制(COW),不立即占用双份内存
当你执行 $b = $a; 时,PHP 并不会立刻复制整个数组数据。内部仅增加对同一哈希表结构的引用计数,$a 和 $b 共享底层存储。只有当其中任一变量被修改(如 $b[] = 1; 或 $b['x'] = 'new';),PHP 才在那一刻复制出独立副本。
- 适用于普通数值、字符串、对象(不含引用属性)等值类型元素
- 内存节省明显:10MB 数组赋值 100 次,只要都不改,仍只占约 10MB
- 可通过 xdebug_debug_zval() 或 memory_get_usage() 验证变化时机
引用赋值(&)彻底共享内存,无复制行为
使用 $b = &$a; 后,两者完全绑定:任一修改都会实时反映在另一个上,且全程零复制开销。此时数组本身、其所有元素(包括嵌套数组)均不分离。
- 注意:即使后续对 $a 进行 unset(),$b 仍有效(因引用计数未归零)
- 若数组含对象,对象本身已是引用语义,& 不改变该层行为,但会影响数组容器本身的绑定关系
- 调试时可用 debug_zval_dump() 查看 refcount 和 is_ref 标志
强制深拷贝需显式遍历,serialize/unserialize 不是最佳解
要真正隔离嵌套结构(如数组内含子数组、对象或资源),不能依赖默认行为。常见误区是用 unserialize(serialize($a)) —— 它虽能深拷贝,但性能差、不支持闭包/资源/部分对象,且触发序列化开销。
立即学习“PHP免费学习笔记(深入)”;
- 推荐递归 clone + array_map:对每个值判断是否为数组,是则递归复制
- 对象需实现 __clone() 方法确保内部引用也被重置
- PHP 7.4+ 可结合 spl_object_id() 辅助检测循环引用,避免无限递归
unset() 和函数传参也影响内存释放时机
数组变量脱离作用域或被 unset,并不等于内存立刻回收。PHP 的垃圾回收(GC)基于引用计数+周期检测,嵌套引用或全局变量持有会延迟释放。
- 函数内接收数组参数默认按值传递(触发 COW),return 后原变量仍存在,除非显式清空
- 大数组处理建议:用 unset($arr) 显式断开引用,再调用 gc_collect_cycles() 加速回收
- CLI 模式下长期运行脚本尤其要注意,避免无意中积累大量 COW 副本










