unset() 是 PHP 釋放變數的標準函式,但其行為常被誤解:unset 只是解除「變數名稱 → 值容器」的綁定,不一定立即釋放記憶體。本文從引用計數機制、複製寫入、陣列元素銷毀、全域變數、靜態變數等面向深度解析。
zval 容器,內含 值、型別、refcount(引用計數)、is_ref(是否屬於引用集合)。$a = $b(值賦值)觸發 Copy-on-Write:兩變數指向同一 zval,refcount++,修改時才真正複製。$a = &$b(引用賦值)建立硬連結:is_ref = true,雙方共享同一 zval,修改即時生效。unset($a):refcount--;當 refcount 歸零 且無其他引用時,記憶體才真正釋放。
1, 'b' => 2, 'c' => 3];
unset($arr['b']); // 移除鍵 'b',陣列變 ['a'=>1, 'c'=>3]
// 數值索引不會自動重排
$arr = [10, 20, 30];
unset($arr[1]); // [0=>10, 2=>30]
$arr = array_values($arr); // 重排索引 [0=>10, 1=>30]
// 釋放整個陣列
unset($arr); // 變數 $arr 消失,底層 zval refcount 歸零則釋放
3) unset($c); // 只對當前請求生效,下次請求 static 重新初始化
}
gc_collect_cycles()),但大量物件時可主動觸發。| 誤區 | 事實 |
|---|---|
| unset 立即釋放記憶體給作業系統 | 釋放回 PHP 記憶體管理器,不一定歸還 OS |
| unset($obj) 會立即呼叫 __destruct | 只有該物件 refcount 歸零時才觸發 |
| 函式內 unset($GLOBALS['x']) 等同 unset 全域 | 正確,但建議直接操作 $GLOBALS 陣列較清晰 |
| unset 陣列元素會重排數值鍵 | 不會,需自行 array_values() |
理解 unset() 底層運作於「引用計數」與「複製寫入」機制,能避免記憶體洩漏、物件提前銷毀、陣列索引錯亂等隱性 Bug。原則:大物件用完即 unset,引用賦值謹慎用,請求結束靠 GC。