You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何PHP中数组实现的哈希表未触发压缩机制?

PHP哈希表压缩未触发的原因分析

我创建了一个大型PHP数组,随后删除了数组开头的大部分元素。根据PHP哈希表实现的原理,PHP不会直接释放内存,仅会将被删除的元素标记为Undef。理论上当我向数组中添加更多元素时,应该达到压缩触发点并看到内存占用降低,但实际并未观察到该现象。

测试代码

<?php

$items = [];

// 哈希表压缩理论上会在2^n大小的节点触发
$size = pow(2, 18);

for($i=0;$i<$size;$i++) {
    $items[$i] = "string $i";
}
// 此时数组达到压缩前的最大表大小,2^18的设置是为了让内存占用变化更明显
printf("mem: %d Mb \n", memory_get_usage(true) / 1024 / 1024);

// 删除除最后一个元素外的所有元素
for($i=0;$i<$size-1;$i++) {
    unset($items[$i]); 
}

// 此时内存占用应该和之前一致,因为被删除元素仅被标记为UNDEF,未释放内存
printf("mem: %d Mb \n", memory_get_usage(true) / 1024 / 1024);

// 理论上添加新元素会触发压缩,内存占用应该下降,但实际没有
$items[] = $i;
printf("mem: %d Mb \n", memory_get_usage(true) / 1024 / 1024);

核心原因分析

  • 压缩触发的优先级低于扩容逻辑:PHP哈希表的压缩需要满足填充率(已使用槽位/总槽位)低于10%且总槽位大于默认最小尺寸(8),你的测试中填充率确实达标,但添加元素的方式触发了扩容判断——因为你用$items[]添加元素时,PHP维护的nNextFreeElement值是262144(原数组最后一个元素索引是262143),新元素会被分配索引262144,这要求哈希表必须保留足够大的槽位来容纳该索引,因此扩容逻辑优先执行,压缩被跳过。
  • 压缩仅在特定操作场景触发:哈希表压缩通常在array_pop()/array_shift()删除元素后、或添加元素时无需扩容且填充率达标时触发。而你的操作中,添加元素需要适配更大的索引,触发了扩容路径,压缩逻辑未被执行。

验证方案

如果修改最后一步的添加方式,使用小范围索引而非自动递增的大索引,比如:

$items[0] = $i;

此时无需扩容,填充率达标,哈希表压缩会被触发,内存占用会明显下降。


内容的提问来源于stack exchange,提问作者Dmitry Lezhnev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 00:03:28