關於 PHP 中 unset() 是否真正釋放記憶體,開發者間向來眾說紛紜。有人認為它不釋放記憶體,有人說只在大變量時才釋放,更有人認為在 PHP 層面討論記憶體毫無意義。本文將通過實例與底層原理,徹底釐清這個問題。
先看一段簡單代碼:
<?php var_dump(memory_get_usage()); $a = "laruence"; var_dump(memory_get_usage()); unset($a); var_dump(memory_get_usage());
輸出可能如下(數值因環境而異):
int(90440) int(90640) int(90472)
變量賦值後記憶體增加 200 位元組,unset() 後只減少 168 位元組,殘留 32 位元組。這 32 位元組究竟去哪了?
PHP 不同於 C 語言,並非只有顯式調用分配 API 才會分配記憶體。對於 $a = "laruence" 這行代碼,隱式分配包括:
因此不能只看表象,必須深入底層機制。
PHP 內部實現了一套類似 C 的記憶體管理 API(emalloc/efree)。調用 emalloc 時,PHP 會向 OS 申請大塊記憶體,再從中切分分配;調用 efree 時,記憶體不會歸還 OS,而是歸入 PHP 維護的空閒列表或緩存列表,供下次複用。這避免了頻繁系統調用,提升性能。
驗證實例(使用 memory_get_usage(true) 獲取真實分配量):
<?php var_dump(memory_get_usage(true)); // 262144 $a = "laruence"; var_dump(memory_get_usage(true)); // 262144 unset($a); var_dump(memory_get_usage(true)); // 262144
可見定義變量時 PHP 未向 OS 申請新記憶體,unset() 後實際分配量也未變,記憶體被內部緩存複用。
為何第一個例子會殘留 32 位元組?關鍵在於 var_dump() 輸出本身會佔用記憶體(嚴格說是輸出緩衝區 Header)。看對比實驗:
<?php
var_dump("I am www.jb51.net");
var_dump(memory_get_usage()); // 90808
$a = "laruence";
var_dump(memory_get_usage()); // 90976
unset($a);
var_dump(memory_get_usage()); // 90808
前後記憶體一致(90808 - 90808 = 0),證明那 32 位元組確實被輸出函數佔用,unset() 實際上已正常釋放變量記憶體。
PHP 的數組與符號表底層均為 Hashtable。Hashtable 初始化只分配少量桶,不足時擴容(resize),只擴不縮。實例:
<?php
var_dump("I am www.jb51.net");
var_dump(memory_get_usage()); // 93560
$array = array_fill(1, 100, "laruence");
foreach ($array as $key => $value) {
${$value . $key} = NULL;
}
var_dump(memory_get_usage()); // 118848
foreach ($array as $key => $value) {
unset(${$value . $key});
}
var_dump(memory_get_usage()); // 104448
創建 100 個變量後記憶體增長,unset 後釋放了變量值記憶體(118848 - 104448 = 14400),但符號表 Hashtable 因擴容而保留的桶結構未縮小,佔用了剩餘記憶體。
unset() 確會釋放變量值(zval)記憶體,歸入 PHP 內部緩存池,供下次分配複用,不歸還 OS。unset,符號表結構仍佔用記憶體。理解機制後,不必過度糾結微小殘留,專注業務邏輯與大對象釋放(如大數組、大字符串)才是性能優化關鍵。