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機制引入的內存增長影響,同樣避免不必要的對變量寫,可以提高代碼運行性能。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章