天天躁日日躁狠狠躁AV麻豆-天天躁人人躁人人躁狂躁-天天澡夜夜澡人人澡-天天影视香色欲综合网-国产成人女人在线视频观看-国产成人女人视频在线观看

淺析PHP原理之變量分離/引用(Variables Separation)

首先我們回顧一下zval的結(jié)構(gòu):
復(fù)制代碼 代碼如下:
struct _zval_struct {
        /* Variable information */
        zvalue_value value; /* value */
        zend_uint refcount;
        zend_uchar type; /* active type */
        zend_uchar is_ref;
};

其中的refcount和is_ref字段我們一直都沒(méi)有介紹過(guò),我們知道php是一個(gè)長(zhǎng)時(shí)間運(yùn)行的服務(wù)器端的腳本解釋器。那么對(duì)于它來(lái)說(shuō),效率和資源占用率是一個(gè)很重要的衡量標(biāo)準(zhǔn),也就是說(shuō),php必須盡量介紹內(nèi)存占用率,考慮下面這段代碼:
復(fù)制代碼 代碼如下:
<?php
   $var = "laruence";
   $var_dup = $var;
   unset($var);
?>

第一行代碼創(chuàng)建了一個(gè)字符串變量,申請(qǐng)了一個(gè)大小為9字節(jié)的內(nèi)存,保存了字符串”laruence”和一個(gè)NULL(/0)的結(jié)尾。
第二行定義了一個(gè)新的字符串變量,并將變量var的值”復(fù)制”給這個(gè)新的變量。
第三行unset了變量var
這樣的代碼在我們平時(shí)的腳本中是很常見(jiàn)的,如果php對(duì)于每一個(gè)變量賦值都重新分配內(nèi)存,copy數(shù)據(jù)的話,那么上面的這段代碼公要申請(qǐng)18個(gè)字節(jié)的內(nèi)存空間,而我們也很容易的看出來(lái),上面的代碼其實(shí)根本沒(méi)有必要申請(qǐng)倆份空間,呵呵,php的開(kāi)發(fā)者也看出來(lái)了:
我們之前講過(guò),php中的變量是用一個(gè)存儲(chǔ)在symbol_table中的符號(hào)名,對(duì)應(yīng)一個(gè)zval來(lái)實(shí)現(xiàn)的,比如對(duì)于上面的第一行代碼,會(huì)在symbol_table中存儲(chǔ)一個(gè)值”var”, 對(duì)應(yīng)的有一個(gè)指針指向一個(gè)zval結(jié)構(gòu),變量值”laruence”保存在這個(gè)zval中,所以不難想象,對(duì)于上面的代碼來(lái)說(shuō),我們完全可以讓”var”和”var_dup”對(duì)應(yīng)的指針都指向同一個(gè)zval就可以了。
php也是這樣做的,這個(gè)時(shí)候就需要介紹我們之前一直沒(méi)有介紹過(guò)的zval結(jié)構(gòu)中的refcount字段了。
refcount,顧名思義,記錄了當(dāng)前的zval被引用的計(jì)數(shù)。
比如對(duì)于代碼:
復(fù)制代碼 代碼如下:
<?php
   $var = 1;
   $var_dup = $var;
?>

第一行,創(chuàng)建了一個(gè)整形變量,變量值是1。 此時(shí)保存整形1的這個(gè)zval的refcount為1。
第二行,創(chuàng)建了一個(gè)新的整形變量,變量也指向剛才創(chuàng)建的zval,并將這個(gè)zval的refcount加1,此時(shí)這個(gè)zval的refcount為2。
php提供了一個(gè)函數(shù)可以幫助我們了解這個(gè)過(guò)程debug_zval_dump:
復(fù)制代碼 代碼如下:
<?php
 $var = 1;
 debug_zval_dump($var);
 $var_dup = $var;
 debug_zval_dump($var);
?>

輸出:
long(1) refcount(2)
long(1) refcount(3

如果你奇怪 ,var的refcount應(yīng)該是1啊?
我們知道,對(duì)于簡(jiǎn)單變量,php是以傳值的形式穿參數(shù)的。也就是說(shuō),當(dāng)執(zhí)行debug_zval_dump($var)的時(shí)候,$var會(huì)以傳值的方式傳遞給debug_zval_dump,也就是會(huì)導(dǎo)致var的refcount加1,所以我們只要能看到,當(dāng)變量賦值給一個(gè)變量以后,能導(dǎo)致zval的refcount加1這個(gè)事實(shí)即可。
現(xiàn)在我們回頭看文章開(kāi)頭的代碼, 當(dāng)執(zhí)行了最后一行unset($var)以后,會(huì)發(fā)生什么呢? 對(duì),既是refcount減1,上代碼:
復(fù)制代碼 代碼如下:
<?php
   $var = "laruence";
   $var_dup = $var;
   unset($var);
   debug_zval_dump($var_dup);
?>

輸出:
string(8) "laruence" refcount(2

但是,對(duì)于下面的代碼呢?
復(fù)制代碼 代碼如下:
<?php
   $var = "laruence";
   $var_dup = $var;
   $var = 1;
?>

很明顯在這段代碼執(zhí)行以后,$var_dup的值應(yīng)該還是”laruence”, 那么這又是怎么實(shí)現(xiàn)的呢?
這就是php的copy on write機(jī)制:
php在修改一個(gè)變量以前,會(huì)首先查看這個(gè)變量的refcount,如果refcount大于1,php就會(huì)執(zhí)行一個(gè)分離的例程, 對(duì)于上面的代碼,當(dāng)執(zhí)行到第三行的時(shí)候,php發(fā)現(xiàn)$var指向的zval的refcount大于1,那么php就會(huì)復(fù)制一個(gè)新的zval出來(lái),將原zval的refcount減1,并修改symbol_table,使得$var和$var_dup分離(Separation)。這個(gè)機(jī)制就是所謂的copy on write(寫時(shí)復(fù)制)。
上代碼測(cè)試:
復(fù)制代碼 代碼如下:
<?php
   $var = "laruence";
   $var_dup = $var;
   $var = 1;
   debug_zval_dump($var);
   debug_zval_dump($var_dup);
?>

輸出:
long(1) refcount(2)
string(8) "laruence" refcount(2

現(xiàn)在我們知道,當(dāng)使用變量復(fù)制的時(shí)候 ,php內(nèi)部并不是真正的復(fù)制,而是采用指向相同的結(jié)構(gòu)來(lái)盡量節(jié)約開(kāi)銷。那么,對(duì)于php中的引用,那又是如何實(shí)現(xiàn)呢?
復(fù)制代碼 代碼如下:
<?php
   $var = "laruence";
   $var_ref = &$var;
   $var_ref = 1;
?>

這段代碼結(jié)束以后,$var也會(huì)被間接的修改為1,這個(gè)過(guò)程稱作(change on write:寫時(shí)改變)。那么ZE是怎么知道,這次的復(fù)制是不需要Separation的呢?
這個(gè)時(shí)候就要用到zval中的is_ref字段了:
對(duì)于上面的代碼,當(dāng)?shù)诙袌?zhí)行以后,$var所代表的zval的refcount變?yōu)?,并且同時(shí)置is_ref為1。
到第三行的時(shí)候,php先檢查var_ref代表的zval的is_ref字段,如果為1,則不分離,大體邏輯示意如下:
復(fù)制代碼 代碼如下:
 if((*val)->is_ref || (*val)->refcount<2){
          //不執(zhí)行Separation
        ... ;//process
  }

但是,問(wèn)題又來(lái)了,對(duì)于如下的代碼,又會(huì)怎樣呢?
復(fù)制代碼 代碼如下:
<?php
   $var = "laruence";
   $var_dup = $var;
   $var_ref = &$var;
?>

對(duì)于上面的代碼,存在一對(duì)copy on write的變量$var和$var_dup, 又有一對(duì)change on write機(jī)制的變量對(duì)$var和$var_ref,這個(gè)情況又是如何運(yùn)作的呢?
當(dāng)?shù)诙袌?zhí)行的時(shí)候,和前面講過(guò)的一樣,$var_dup 和 $var 指向相同的zval, refcount為2.
當(dāng)執(zhí)行第三行的時(shí)候,php發(fā)現(xiàn)要操作的zval的refcount大于1,則,php會(huì)執(zhí)行Separation, 將$var_dup分離出去,并將$var和$var_ref做change on write關(guān)聯(lián)。也就是,refcount=2, is_ref=1;
基于這樣的分析,我們就可以讓debug_zval_dump出refcount為1的結(jié)果來(lái):
復(fù)制代碼 代碼如下:
<?php
     $var = "laruence";
    $var_dup = &$var;
     debug_zval_dump($var);
?>

輸出:
string(8) "laruence" refcount(1

詳細(xì)原因,讀者你只要稍加分析就能得出,我就不越俎代庖了。;)
這次我們介紹了php的變量分離機(jī)制,下次我會(huì)繼續(xù)介紹如果在擴(kuò)展中接收和傳出php腳本中的參數(shù)。

php技術(shù)淺析PHP原理之變量分離/引用(Variables Separation),轉(zhuǎn)載需保留來(lái)源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 久久精品九九亚洲精品天堂 | 欧美xxxxx九色视频免费观看 | 动漫美女被到爽了流漫画 | 狠狠鲁 我喜欢 | 国产精品久久久久久久久LI无码 | 色丁香婷婷综合缴情综 | 国产精品高潮呻吟AV久久96 | 91国内精品久久久久免费影院 | 国产国产乱老熟女视频网站97 | 国产成人片视频一区二区青青 | 一个人免费观看HD完整版 | 国产51麻豆二区精品AV视频 | 久草在线福利资站免费视频 | max girls 大感谢祭 | 午夜精品久久久久久影视riav | 中国少妇内射XXXX狠干 | 国产精品自在在线午夜蜜芽tv在线 | 日韩av国产av欧美天堂社区 | 国产亚洲精品网站在线视频 | 成年人免费观看视频网站 | 日本高清免费观看 | 久久亚洲午夜牛牛影视 | 中文在线免费看视频 | 夜色资源站国产www在线视频 | 久久九九有精品国产23百花影院 | 簧片在线免费观看 | 国产99久久亚洲综合精品西瓜tv | 二次元美女扒开内裤喷水 | 亚洲免费无码中文在线亚洲在 | 伦理片97影视网 | 中文视频在线观看 | 玖玖热视频一区二区人妻 | 亚洲欧美一区二区三区四区 | 亚洲欧美日本国产在线观18 | 我解开了岳的乳第一个女人 | av在线观看网站免费 | 成人免费一级毛片在线播放视频 | 国产美女又黄又爽又色视频网站 | 国产免费人成在线视频视频 | 亚洲薄码区 | 国内精品久久久久久久试看 |