PHP 7 数组更快是因为内存布局从多块变为一块:将元数据、hash 槽和 zval 元素紧凑排布在连续内存中,提升 CPU 缓存命中率,降低空数组内存占用至约 56 字节。

PHP 7 数组为什么快:内存布局从多块变成一块
PHP 5 的 zval 结构体本身大(64 位下 24 字节),而数组还额外维护一个独立的 hash 表结构,HashTable 和实际元素数据分开放在不同内存块。CPU 缓存命中率低,每次遍历或查找都要跳转地址。
PHP 7 把数组的元数据(如 arData、nTableSize)、hash 映射槽(arHash)和元素值(zval)全部紧凑排布在同一块连续内存里。实测单个空数组内存占用从 PHP 5 的 ~100 字节降到 PHP 7 的 ~56 字节。
- 对小数组(
count($arr) ),PHP 7 直接用线性探测替代传统 hash 表,避免取模和冲突处理开销 - 所有
zval不再带引用计数字段refcount__gc和is_ref标志位,改由外部容器统一管理,结构更轻量 - 键名字符串不再单独分配内存,而是与数组结构体“内联”存放(
zend_string头部 + 数据紧贴)
hash 冲突处理机制变了:从链表拉链到开放寻址
PHP 5 用拉链法(chaining):每个 hash 槽指向一个单向链表,冲突时往链表尾追加。最坏情况退化为 O(n),且链表节点分散在堆上,缓存不友好。
PHP 7 改用开放寻址(open addressing)+ 线性探测(linear probing)。所有 hash 槽是固定大小的连续数组,冲突时顺序往后找下一个空槽。只要负载因子控制得当(默认 ≤ 0.75),平均查找只需 1–2 次内存访问。
- hash 表扩容不再重建整个结构,而是复制旧槽位并重哈希,但新结构仍保持连续内存
-
zend_hash_find在键存在时几乎总是常数时间;PHP 5 的_zend_hash_find却要遍历链表 - 注意:大量重复
unset()后未rehash,可能导致稀疏表,线性探测步数上升——这不是 bug,是设计权衡
foreach 性能翻倍的关键:避免中间 zval 拷贝
PHP 5 的 foreach 实际上会先对数组做一次浅拷贝(copy-on-write 触发前就分配新 HashTable),再遍历副本,导致额外内存分配和指针跳转。
PHP 7 的 foreach 直接迭代原数组的 arData 底层数组,配合新的 zend_array 迭代器协议,完全绕过 hash 查找路径。即使数组被修改,也只在必要时触发 zend_hash_iterator_update 局部修正。
- 对只读遍历场景,PHP 7 几乎零额外内存分配
- 如果循环体内有
$arr[] = ...或unset($arr[$k]),PHP 7 会标记迭代器为“不可靠”,后续行为按需 fallback,但不会提前拷贝整张表 - 不要依赖
foreach中修改数组后“还能继续遍历”的行为——PHP 7 更严格,行为可能和 PHP 5 不一致
什么时候 PHP 7 数组优势不明显?
不是所有数组操作都受益。以下场景 PHP 7 相比 PHP 5 提升有限甚至持平:
- 超大数组(> 100 万元素)且随机 key 分布极差,hash 冲突率高,线性探测步数上升
- 频繁混合使用数字键和字符串键(比如
[0 => 'a', 'foo' => 'b']),PHP 7 仍需维护两套索引逻辑 - 大量调用
array_keys()、array_values()这类重建数组的操作,性能瓶颈转移到新数组分配,而非原结构访问 - 启用了
opcache.enable=1但未启用opcache.optimization_level=0xffffffff,部分数组优化无法生效
真正的加速来自底层内存局部性改善和指令路径缩短,而不是某个函数突然变快。如果你的代码卡在 json_encode 或 DB 查询上,换 PHP 7 对数组本身的优化几乎感知不到。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











