亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > PHP > 正文

PHP 之 寫時復制介紹(Copy On Write)

2020-03-22 20:16:19
字體:
來源:轉載
供稿:網友
在開始之前,我們可以先看一段簡單的代碼:復制代碼 代碼如下:
?php //例一
$foo = 1;
$bar = $foo;
echo $foo + $bar;
?
執行這段代碼,會打印出數字2。從內存的角度來分析一下這段代碼“可能”是這樣執行的:分配一塊內存給foo變量,里面存儲一個1; 再分配一塊內存給bar變量,也存一個1,最后計算出結果輸出。事實上,我們發現foo和bar變量因為值相同,完全可以使用同一塊內存,這樣,內存的使用就節省了一個1,并且,還省去了分配內存和管理內存地址的計算開銷。沒錯,很多涉及到內存管理的系統,都實現了這種相同值共享內存的策略:寫時復制很多時候,我們會因為一些術語而對其概念產生莫測高深的恐懼,而其實,他們的基本原理往往非常簡單。本小節將介紹PHP中寫時復制這種策略的實現:寫時復制(Copy on Write,也縮寫為COW)的應用場景非常多, 比如Linux中對進程復制中內存使用的優化,在各種編程語言中,如C++的STL等等中均有類似的應用。 COW是常用的優化手段,可以歸類于:資源延遲分配。只有在真正需要使用資源時才占用資源, 寫時復制通常能減少資源的占用。 注: 為節省篇幅,下文將統一使用COW來表示“寫時復制”;推遲內存復制的優化 正如前面所說,PHP中的COW可以簡單描述為:如果通過賦值的方式賦值給變量時不會申請新內存來存放新變量所保存的值,而是簡單的通過一個計數器來共用內存,只有在其中的一個引用指向變量的值發生變化時才申請新空間來保存值內容以減少對內存的占用。在很多場景下PHP都COW進行內存的優化。比如:變量的多次賦值、函數參數傳遞,并在函數體內修改實參等。下面讓我們看一個查看內存的例子,可以更容易看到COW在內存使用優化方面的明顯作用:復制代碼 代碼如下:
?php //例二
$j = 1;
var_dump(memory_get_usage());

$tipi = array_fill(0, 100000, 'php-internal');
var_dump(memory_get_usage());

$tipi_copy = $tipi;
var_dump(memory_get_usage());

foreach($tipi_copy as $i){
$j += count($i);
}
var_dump(memory_get_usage());

//-----執行結果-----
$ php t.php
int(630904)
int(10479840)
int(10479944)
int(10480040)
上面的代碼比較典型的突出了COW的作用,在數組變量$tipi被賦值給$tipi_copy時,內存的使用并沒有立刻增加一半,在循環遍歷數$tipi_copy時也沒有發生顯著變化,在這里$tipi_copy和$tipi變量的數據共同指向同一塊內存,而沒有復制。 也就是說,即使我們不使用引用,一個變量被賦值后,只要我們不改變變量的值 ,也不會新申請內存用來存放數據。據此我們很容易就可以想到一些COW可以非常有效的控制內存使用的場景:只是使用變量進行計算而很少對其進行修改操作,如函數參數的傳遞,大數組的復制等等等不需要改變變量值的情形。復制分離變化的值 多個相同值的變量共用同一塊內存的確節省了內存空間,但變量的值是會發生變化的,如果在上面的例子中,指向同一內存的值發生了變化(或者可能發生變化),就需要將變化的值“分離”出去,這個“分離”的操作,就是“復制”。 在PHP中,Zend引擎為了區別同一個zval地址是否被多個變量共享,引入了ref_count和is_ref兩個變量進行標識:復制代碼 代碼如下:
ref_count和is_ref是定義于zval結構體中(見第一章第一小節)
is_ref標識是不是用戶使用 & 的強制引用;
ref_count是引用計數,用于標識此zval被多少個變量引用,即COW的自動引用,為0時會被銷毀;
關于這兩個變量的更多內容,跳轉閱讀:第三章第六節:變量的賦值和銷毀的實現。
注:由此可見, $a=$b; 與 $a= 在PHP對內存的使用上沒有區別(值不變化時);
下面我們把例二稍做變化:如果$copy的值發生了變化,會發生什么?:復制代碼 代碼如下:
?php //例三
//$tipi = array_fill(0, 3, 'php-internal');
//這里不再使用array_fill來填充 ,為什么?
$tipi[0] = 'php-internal';
$tipi[1] = 'php-internal';
$tipi[2] = 'php-internal';
var_dump(memory_get_usage());

$copy = $tipi;
xdebug_debug_zval('tipi', 'copy');
var_dump(memory_get_usage());

$copy[0] = 'php-internal';
xdebug_debug_zval('tipi', 'copy');
var_dump(memory_get_usage());

//-----執行結果-----
$ php t.php
int(629384)
tipi: (refcount=2, is_ref=0)=array (0 = (refcount=1, is_ref=0)='php-internal',
1 = (refcount=1, is_ref=0)='php-internal',
2 = (refcount=1, is_ref=0)='php-internal')
copy: (refcount=2, is_ref=0)=array (0 = (refcount=1, is_ref=0)='php-internal',
1 = (refcount=1, is_ref=0)='php-internal',
2 = (refcount=1, is_ref=0)='php-internal')
int(629512)
tipi: (refcount=1, is_ref=0)=array (0 = (refcount=1, is_ref=0)='php-internal',
1 = (refcount=2, is_ref=0)='php-internal',
2 = (refcount=2, is_ref=0)='php-internal')
copy: (refcount=1, is_ref=0)=array (0 = (refcount=1, is_ref=0)='php-internal',
1 = (refcount=2, is_ref=0)='php-internal',
2 = (refcount=2, is_ref=0)='php-internal')
int(630088)
在這個例子中,我們可以發現以下特點:$copy = $tipi;這種基本的賦值操作會觸發COW的內存“共享”,不會產生內存復制;COW的粒度為zval結構,由PHP中變量全部基于zval,所以COW的作用范圍是全部的變量,而對于zval結構體組成的集合(如數組和對象等),在需要復制內存時,將復雜對象分解為最小粒度來處理。這樣可以使內存中復雜對象中某一部分做修改時,不必將該對象的所有元素全部“分離復制”出一份內存拷貝;復制代碼 代碼如下:
array_fill()填充數組時也采用了COW的策略,可能會影響對本例的演示,感興趣的讀者可以 閱讀:$PHP_SRC/ext/standard/array.c中PHP_FUNCTION(array_fill)的實現。

xdebug_debug_zval()是xdebug擴展中的一個函數,用于輸出變量在zend內部的引用信息。 如果你沒有安裝xdebug擴展,也可以使用debug_zval_dump()來代替。 參考:http://www.php.net/manual/zh/function.debug-zval-dump.php
實現寫時復制 看完上面的三個例子,相信大家也可以了解到PHP中COW的實現原理: PHP中的COW基于引用計數ref_count和is_ref實現,多一個變量指針,就將ref_count加1, 反之減去1,減到0就銷毀;同理,多一個強制引用&,就將is_ref加1,反之減去1。這里有一個比較典型的例子:復制代碼 代碼如下:
?php //例四
$foo = 1;
xdebug_debug_zval('foo');
$bar = $foo;
xdebug_debug_zval('foo');
$bar = 2;
xdebug_debug_zval('foo');
?
//-----執行結果-----
foo: (refcount=1, is_ref=0)=1
foo: (refcount=2, is_ref=0)=1
foo: (refcount=1, is_ref=0)=1
經過前面對變量章節的介紹,我們知道當$foo被賦值時,$foo變量的值的只由$foo變量指向。當$foo的值被賦給$bar時,PHP并沒有將內存復制一份交給$bar,而是把$foo和$bar指向同一個地址。同時引用計數增加1,也就是新的2。隨后,我們更改了$bar的值,這時如果直接需該$bar變量指向的內存,則$foo的值也會跟著改變。這不是我們想要的結果。于是,PHP內核將內存復制出來一份,并將其值更新為賦值的:2(這個操作也稱為變量分離操作),同時原$foo變量指向的內存只有$foo指向,所以引用計數更新為:refcount=1。 看上去很簡單,但由于&運算符的存在,實際的情形要復雜的多。見下面的例子:


圖6.6 &操作符引起的內存復制分離 從這個例子可以看出PHP對&運算符的一個容易出問題的處理:當 $beauty= 時,兩個變量本質上都變成了引用類型,導致看上去的普通變量$pan, 在某些內部處理中與&$pan行為相同,尤其是在數組元素中使用引用變量,很容易引發問題。(見最后的例子) PHP的大多數工作都是進行文本處理,而變量是載體,不同類型的變量的使用貫穿著PHP的生命周期,變量的COW策略也就體現了Zend引擎對變量及其內存處理,具體可以參閱源碼文件相關的內容:復制代碼 代碼如下:
Zend/zend_execute.c
========================================
zend_assign_to_variable_reference();
zend_assign_to_variable();
zend_assign_to_object();
zend_assign_to_variable();

//以及下列宏定義的使用
Zend/zend.h
========================================
#define Z_REFCOUNT(z) Z_REFCOUNT_P(&(z))
#define Z_SET_REFCOUNT(z, rc) Z_SET_REFCOUNT_P(&(z), rc)
#define Z_ADDREF(z) Z_ADDREF_P(&(z))
#define Z_DELREF(z) Z_DELREF_P(&(z))
#define Z_ISREF(z) Z_ISREF_P(&(z))
#define Z_SET_ISREF(z) Z_SET_ISREF_P(&(z))
#define Z_UNSET_ISREF(z) Z_UNSET_ISREF_P(&(z))
#define Z_SET_ISREF_TO(z, isref) Z_SET_ISREF_TO_P(&(z), isref)
最后,請慎用引用& 引用和前面提到的變量的引用計數和PHP中的引用并不是同一個東西,引用和C語言中的指針的類似,他們都可以通過不同的標示訪問到同樣的內容,但是PHP的引用則只是簡單的變量別名,沒有C指令的靈活性和限制。 PHP中有非常多讓人覺得意外的行為,有些因為歷史原因,不能破壞兼容而選擇暫時不修復,或者有的使用場景比較少。在PHP中只能盡量的避開這些陷阱。例如下面這個例子。 由于引用操作符會導致PHP的COW策略優化,所以使用引用也需要對引用的行為有明確的認識才不至于誤用,避免帶來一些比較難以理解的的Bug。如果您認為您已經足夠了解了PHP中的引用,可以嘗試解釋下面這個例子:復制代碼 代碼如下:
?php
$foo['love'] = 1;
$bar = &$foo['love'];
$tipi = $foo;
$tipi['love'] = '2';
echo $foo['love'];
這個例子最后會輸出 2 , 大家會非常驚訝于$tipi怎么會影響到$foo, $bar變量的引用操作,將$foo['love']污染變成了引用,從而Zend沒有對$tipi['love']的修改產生內存的復制分離。PHP教程

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美国产亚洲精品久久久8v| 日本19禁啪啪免费观看www| 亚洲美女av在线| 久久精品国产久精国产一老狼| 久久亚洲精品中文字幕冲田杏梨| 亚洲精品天天看| 亚洲的天堂在线中文字幕| 中文字幕成人精品久久不卡| 亚洲香蕉成视频在线观看| 国产精品99久久久久久www| 久久久精品欧美| 91久久久久久久| 一本色道久久88综合亚洲精品ⅰ| 久久精品视频在线播放| 欧美激情视频网址| 2019中文在线观看| xx视频.9999.com| 色久欧美在线视频观看| 欧美成人精品一区| 欧洲亚洲免费视频| 亚洲精品日韩久久久| 国产精品久久久久高潮| 欧美性xxxx极品hd欧美风情| 欧美一级大片视频| 欧美体内谢she精2性欧美| 欧美激情久久久久| 欧美xxxx综合视频| 日韩在线中文视频| 国产91精品青草社区| 国产精品电影网站| 亚洲男人天堂九九视频| 亚洲精品自产拍| 国产精品88a∨| 欧美成人午夜影院| 欧美日韩美女视频| 日韩欧美在线观看视频| 亚洲va欧美va国产综合剧情| 国产ts人妖一区二区三区| 91欧美精品成人综合在线观看| 久久精品99久久久久久久久| 久久久综合免费视频| 91国偷自产一区二区三区的观看方式| 久久在线视频在线| 国产精品美女免费看| 欧美性69xxxx肥| 国内精品模特av私拍在线观看| 日韩精品中文字幕在线播放| 91高清视频免费| 精品久久久香蕉免费精品视频| 久久久91精品| 一区二区三区www| 国内精品国产三级国产在线专| 欧美性极品xxxx娇小| 久久久久久久久综合| 国产丝袜精品视频| 国产精品九九久久久久久久| 98精品国产自产在线观看| 奇米四色中文综合久久| 91久久精品国产91久久性色| 国产精品∨欧美精品v日韩精品| 亚洲激情电影中文字幕| 久热精品视频在线观看| 欧美精品videos另类日本| 国产精品丝袜久久久久久不卡| 538国产精品视频一区二区| 91精品国产综合久久香蕉最新版| 国产精品影片在线观看| 久久久女人电视剧免费播放下载| 日韩av黄色在线观看| 亚洲人成网7777777国产| 日韩欧美在线视频日韩欧美在线视频| 热久久美女精品天天吊色| 91精品视频免费观看| 精品色蜜蜜精品视频在线观看| 91中文字幕在线观看| 久久久久久久国产| 日韩女在线观看| 日本一区二三区好的精华液| 亚洲va欧美va国产综合剧情| 日韩激情av在线免费观看| 在线亚洲欧美视频| 国产视频在线观看一区二区| 日韩综合视频在线观看| 日韩av三级在线观看| 亚洲最大中文字幕| 日本免费久久高清视频| 91中文字幕在线| 8x拔播拔播x8国产精品| 精品网站999www| 亚洲аv电影天堂网| 亚洲激情视频在线观看| 久久精品视频99| 国产一区二区久久精品| 日韩大片在线观看视频| 亚洲精品影视在线观看| 国产欧美 在线欧美| 欧美日韩一区免费| 亚洲精品永久免费| 亚洲一区二区久久| 成人福利网站在线观看11| 欧美俄罗斯乱妇| 国内精品模特av私拍在线观看| 欧美另类69精品久久久久9999| 国产91精品视频在线观看| 午夜精品久久久久久久男人的天堂| 日韩视频中文字幕| 国产精品夜色7777狼人| 国产精品偷伦免费视频观看的| 91精品在线看| 欧美精品18videos性欧美| 久久免费成人精品视频| 久久精品国产视频| 国产精品久久久久久久app| 日韩第一页在线| 中文字幕久久久av一区| 91亚洲精品视频| 欧美日韩在线看| 久久99精品国产99久久6尤物| 欧美视频在线视频| 欧美性理论片在线观看片免费| 色偷偷av一区二区三区| 91爱视频在线| 青青草国产精品一区二区| 欧美日韩福利电影| 欧美又大又粗又长| 国产精品人成电影| 亚洲视频一区二区三区| 亚洲www视频| 国产精品入口尤物| 日本午夜精品理论片a级appf发布| 69国产精品成人在线播放| 日韩av最新在线| 国产视频精品在线| 久久福利视频导航| 久久精品一本久久99精品| 日韩免费黄色av| 国产精品女人网站| 欧美老女人www| 91视频国产精品| 992tv成人免费视频| 51精品在线观看| 亚洲国产精彩中文乱码av在线播放| 欧美日韩国产一中文字不卡| 亚洲深夜福利网站| 91老司机在线| 日韩欧美国产激情| 奇门遁甲1982国语版免费观看高清| 午夜精品久久久久久久99热浪潮| 97人人爽人人喊人人模波多| 91网站在线免费观看| 亚洲成人黄色在线观看| 国产69精品久久久久9999| 国产主播精品在线| 97久久精品在线| 国产精品女人久久久久久| 韩剧1988在线观看免费完整版| 精品中文字幕乱| 亚洲精品国产综合区久久久久久久| 国产精品黄视频| 国产精品日日做人人爱| 九九久久久久99精品| 欧美精品福利在线| 亚洲无线码在线一区观看| 亚洲理论在线a中文字幕|