PHP 是托管語言,記憶體管理由引擎自動完成。PHP 5.2 到 5.3 的 GC 演進,解決了長期困擾長運行腳本的**循環引用記憶體洩漏**問題。
所有 PHP 變數在 C 層統一用 zval (_zval_struct) 表示:
struct _zval_struct {
zvalue_value value; // 聯合體:lval/dval/str/ht/obj
zend_uint refcount__gc; // 引用計數 (GC 核心)
zend_uchar type; // 型別標記
zend_uchar is_ref__gc; // 是否為引用 (&)
};
refcount__gc 記錄有多少變數/符號表指向此 zval。
unset/離開作用域 → refcount--致命缺陷:循環引用無法回收
$a = [];
$a[] = &$a; // $a[0] 引用 $a 自己
unset($a); // 全域符號表移除 $a,但 $a 的 zval refcount=1 (被自己引用)
// 此 zval 永遠不為 0 → 記憶體洩漏,直到進程結束
短生命週期 Web 請求 (毫秒級) 掩蓋了此問題;但 **CLI 長運行腳本、Daemon、Worker** 會累積洩漏導致 OOM。
基於 IBM 論文 Concurrent Cycle Collection in Reference Counted Systems 實作。
gc_collect_cycles()。zend.enable_gc (開關)、gc_collect_cycles() (強制)、源碼修改 GC_ROOT_BUFFER_MAX_ENTRIES 重編譯調整緩衝區大小。gc_collect_cycles() (如批次處理每 1000 條)。Web 請求生命週期短,影響可忽略。WeakMap/WeakReference,支援弱引用,進一步減少循環引用風險。$b = $a 不複製內存,寫入時才複製,善用此特性避免不必要 &。unset** 或重賦值 $arr = [] 釋放引用。memory_get_usage()、gc_status() (PHP 7.3+) 觀察 GC 行為。ini_set('zend.enable_gc', 0) 關閉 GC,**但必須確保無循環引用**,否則洩漏不可逆。從「請求結束大掃除」到「運行期增量循環清理」,PHP GC 演進讓長運行 PHP 應用成為現實。