为何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
相关产品推荐
相关产品推荐

