PHP 與 C# 均為高階語言,但對「值類型」的記憶體管理機制截然不同。理解這一差異,對於從 C# 轉 PHP、或編寫高效能 PHP 擴展、分析變數引用行為至關重要。
<?php
$a = 2;
$b = $a; // 此時 $b 指向與 $a 相同的 zval 容器(引用計數 refcount=2)
$a = 5; // 觸發寫時複製:Zend Engine 偵測 refcount > 1,
// 為 $a 分配新 zval,複製原值 (2) 給 $b 保留,
// $a 的新 zval 設為 5,refcount 歸 1。
// 結果:$a=5, $b=2
value、type、refcount__gc、is_ref__gc。$b = $a 只增加 refcount,不複製記憶體。refcount > 1,引擎分離一份新 zval 給修改者(Copy-on-Write)。int a = 2;
int b = a; // 直接在堆疊上為 b 分配 4 位元組,複製 a 的值 (2)
// 此時 a、b 佔用獨立記憶體位址,互不影響
a = 5; // 僅修改 a 的堆疊槽位,b 仍為 2
int, struct, enum 等)直接儲存於堆疊(或內嵌於物件/陣列中)。class, interface, delegate, string, 陣列)儲存於堆積,變數持有參考(指標),賦值複製參考而非物件。| 特性 | PHP (標量/陣列) | C# 值類型 | C# 參考類型 |
|---|---|---|---|
| 賦值行為 | COW(引用計數+寫時複製) | 完整位元複製 | 複製參考(指標) |
| 修改原變數影響副本? | 否(觸發 COW 分離) | 否(獨立記憶體) | 是(共享同一物件) |
| 記憶體位置 | 堆積(zval 由引擎管理) | 堆疊/內嵌 | 堆積(物件)、堆疊(參考) |
| 大型資料傳遞開銷 | 極低(未修改零複製) | 高(每次複製整個結構體) | 低(複製參考) |
| 傳址/參考修改 | function(&$v) 強制共享 | ref/out 參數 | 預設即為參考 |
function process(array $data) {
// 僅讀取:零複製,極快
$sum = array_sum($data);
return $sum;
}
$big = range(1, 1000000);
process($big); // 未修改 $big,全程共享同一 zval 陣列
struct BigData { public int[] Items; } // 結構體包含參考類型陣列
// 傳遞 BigData 會複製整個結構體(含陣列參考),若頻繁呼叫建議改 class 或用 ref
function modify(&$arr) { $arr[] = 99; }
$a = [1,2,3];
modify($a); // $a 變 [1,2,3,99],refcount=1 時直接修改,無 COW
Bucket/變數表,減少指標間接存取。zend_string 引入字串駐留與引用計數,字串複製亦為 COW。PHP 的寫時複製讓值類型傳遞「看起來像傳值,實際上未修改零成本」,極適合動態語言的靈活語意;C# 的值類型獨立堆疊分配提供確定性的記憶體佈局與複製語意,適合高效能、資料導向設計。兩者無優劣,僅服務於不同語言哲學與執行模型。