1. 程式人生 > >PHP寫時複製技術

PHP寫時複製技術

在PHP 核心中同樣使用了寫時複製機制來避免在賦值時導致記憶體增加 

什麼是寫時複製 COW(Copy On Write)?
答:在複製一個物件的時候並不是真正的把原先的物件複製到記憶體的另外一個位置上,而是在新物件的記憶體對映表中設定一個指標,指向源物件的位置,並把那塊記憶體的Copy-On-Write位設定為1.這樣,在對新的物件執行讀操作的時候,記憶體資料不發生任何變動,直接執行讀操作;而在對新的物件執行寫操作時,將真正的物件複製到新的記憶體地址中,並修改新物件的記憶體對映表指向這個新的位置,並在新的記憶體位置上執行寫操作。

這個技術需要跟虛擬記憶體和分頁同時使用,好處就是在執行復制操作時因為不是真正的記憶體複製,而只是建立了一個指標,因而大大提高效率。但這不是一直成立的,如果在複製新物件之後,大部分物件都還需要繼續進行寫操作會產生大量的分頁錯誤,得不償失。所以COW高效的情況只是在複製新物件之後,在一小部分的記憶體分頁上進行寫操作。

比如我們在使用foreach迴圈體時,可以發現其中的奧祕,示例程式碼:

	$m1 = memory_get_usage();
$str=<<<EOF
aaaaaaaaaaaaaa
aaaaaaaaaaaaaa
aaaaaaaaaaaaaa
EOF;
$arr = explode("\n", $str);
$count=0;
foreach($arr as $v){
    $count++;
    //$v='aaaaaaaaaaaaaa';
}
$m2 = memory_get_usage();
echo $m2-$m1;

當我們執行此程式碼時會得到記憶體佔用為:788 

$m1 = memory_get_usage();
$str=<<<EOF
aaaaaaaaaaaaaa
aaaaaaaaaaaaaa
aaaaaaaaaaaaaa
EOF;
$arr = explode("\n", $str);
$count=0;
foreach($arr as $v){
$count++;
$v='aaaaaaaaaaaaaa';
}
$m2 = memory_get_usage();
echo $m2-$m1;

當我們取消 //$v='aaaaaaaaaaaaaa';  的註釋,此時記憶體佔用數值為:840,注意記憶體增長了

$m1 = memory_get_usage();
$str=<<<EOF
aaaaaaaaaaaaaa
aaaaaaaaaaaaaa
aaaaaaaaaaaaaa
EOF;
$arr = explode("\n", $str);
$count=0;
foreach($arr as &$v){
$count++;
//$v='aaaaaaaaaaaaaa';
}
$m2 = memory_get_usage();
echo $m2-$m1;
當我們將foreach中的$v 改寫為 &$v 時,不管是否註釋迴圈體中對$v的註釋,我們都可以得到記憶體佔用為:788
這裡就說明了COW機制的介入,當我們在foreach迴圈中純粹的只用到對$v 的讀操作時,PHP核心會將$v這個變數的記憶體地址指向到$arr中陣列這一索引的記憶體地址,並沒有將陣列中的資料複製一份給到變數$v,此時記憶體佔用情況和使用&$v 是一樣的。但當我們在迴圈體內對$v進行寫操作時,寫時複製機制就被激活了,此時PHP會重新開闢一段記憶體空間給到$v變數,而將原先$v指向陣列的記憶體地址給斷開了,此時記憶體必然就會增長了。
這裡可以得出另外一個結論:當我們在讀取大資料的時候,要注意COW機制引入的記憶體增長影響,同樣避免不必要的對變數寫,可以提高程式碼執行效能。