PHP 5.3 引入專門的 循環垃圾回收器 (Cyclic GC) 解決參考計數無法處理的循環引用洩漏。對於長運行腳本 (守護進程、Queue Worker、CLI 批次任務),理解 GC 行為關係到記憶體穩定與效能。
每個 PHP 變數在引擎中由 zval 容器儲存 (PHP 7 後為 zend_value + zend_refcounted):
struct zval {
zend_value value; // 實際資料 (整數、雙精度、字串指標、陣列指標、物件指標...)
zend_refcounted *ref; // 參考計數與型別資訊 (GC 相關)
// PHP 7+ 合併為單一結構,概念不變
};
關鍵欄位:
& 參考 (而非複製)。IS_LONG, IS_DOUBLE, IS_STRING, IS_ARRAY, IS_OBJECT, IS_REFERENCE 等。<?php
$a = 'hello'; // refcount=1, is_ref=0
$b = $a; // refcount=2 (共享同一 zval)
$b .= ' world'; // 寫入觸發分離:$b 新建 zval, $a 保持不變, refcount 各自=1
參考賦值 (&) 會將 is_ref=1,強制共享同一容器,寫入直接影響所有綁定變數。
<?php
$a = ['one'];
$a[] = &$a; // $a[1] 參考 $a 自身 → 循環
unset($a); // 符號表移除 $a,但容器 refcount=1 (來自 $a[1]),無法釋放
// 請求結束前記憶體永不釋放 → 長運行腳本洩漏
refcount 增加 → 非垃圾,不進緩衝區。refcount 減為 0 → 立即釋放,不進緩衝區。refcount 減少但 > 0 → 可能形成循環,放入根緩衝區 (紫色標記)。gc_collect_cycles()) → 執行 標記-掃描:
refcount -1 (灰色),再遞歸遍歷屬性/元素,最終 refcount 為 0 的為垃圾 (白色),>0 的為存活 (黑色)。refcount。| 設定 | 說明 | 預設 |
|---|---|---|
zend.enable_gc | php.ini 啟用/停用 GC | On |
gc.collect_cycles() | 強制執行循環回收,回傳釋放數量 | - |
gc_enable() / gc_disable() | 執行期動態開關 | On |
gc_status() | PHP 7.3+ 取得 GC 狀態 (runs, collected, thresholds) | - |
<?php
class Foo { public $var = '3.14159'; public $self; }
$base = memory_get_usage();
for ($i = 0; $i < 1_000_000; $i++) {
$a = new Foo();
$a->self = $a; // 循環引用
if ($i % 100_000 === 0) {
echo "$i: " . (memory_get_usage() - $base) . " bytes\n";
}
}
echo "Peak: " . memory_get_peak_usage() . "\n";
結果:啟用 GC 記憶體穩定在 ~30MB;停用 GC 線性增長至 >1GB 導致 OOM。執行時間增加 ~5-7% (GC 執行開銷)。
gc_collect_cycles(),避免緩衝區滿時大量掃描造成延遲尖峰。WeakReference (PHP 7.4+) 打破父子循環;觀察者模式用 SplObjectStorage + 弱引用。gc_status(),觀察 collected / runs 比率,調整 zend.gc_probability (PHP 8.1+)。unset() 大變數並 gc_collect_cycles()。WeakMap、WeakReference<?php
class Cache {
private WeakMap $map; // Key 為物件,不增加 refcount
public function __construct() { $this->map = new WeakMap(); }
public function set(object $key, mixed $val): void { $this->map[$key] = $val; }
public function get(object $key): mixed { return $this->map[$key] ?? null; }
}
// 當 $key 無其他參考時,自動從 WeakMap 移除,不阻礙 GC
完整效能測試腳本與 GC 視覺化工具見 GitHub。