什麼是寫時複製(Copy On Write)?
答:在複製一個對象的時候並不是真正的把原先的對象複製到記憶體的另外一個位置上,而是在新對象的記憶體映射表中設定一個指標,指向來源物件的位置,並把那塊記憶體的Copy-On-Write位設定為1.這樣,在對新的對象執行讀操作的時候,記憶體資料不發生任何變動,直接執行讀操作;而在對新的對象執行寫操作時,將真正的對象複製到新的記憶體位址中,並修改新對象的記憶體映射表指向這個新的位置,並在新的記憶體位置上執行寫操作。
這個技術需要跟虛擬記憶體和分頁同時使用,好處就是在執行複製操作時因為不是真正的記憶體複製,而只是建立了一個指標,因而大大提高效率。但這不是一直成立的,如果在複製新對象之後,大部分對象都還需要繼續進行寫操作會產生大量的分頁錯誤,得不償失。所以COW高效的情況只是在複製新對象之後,在一小部分的記憶體分頁上進行寫操作。
在PHP 核心中同樣使用了寫時複製機制來避免在賦值時導致記憶體增加,比如我們在使用foreach迴圈體時,可以發現其中的奧秘,範例程式碼:
複製代碼 代碼如下:
$m1 = memory_get_usage();
$str=<<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=<<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=<<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機制引入的記憶體增長影響,同樣避免不必要的對變數寫,可以提高代碼運行效能。
http://www.bkjia.com/PHPjc/769674.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/769674.htmlTechArticle什麼是寫時複製(Copy On Write)? 答:在複製一個對象的時候並不是真正的把原先的對象複製到記憶體的另外一個位置上,而是在新對象的內...