深入理解PHP原理之變數分離/引用(Variables Separation)

來源:互聯網
上載者:User

這節我們就接著前面的文章,繼續介紹PHP中變數分離和引用的概念:

首先我們回顧一下zval的結構:

 
  1. struct _zval_struct {
  2.         /* Variable information */
  3.         zvalue_value value; /* value */
  4.         zend_uint refcount;
  5.         zend_uchar type; /* active type */
  6.         zend_uchar is_ref;
  7. }
    1. ;

其中的refcount和is_ref欄位我們一直都沒有介紹過,我們知道PHP是一個長時間啟動並執行伺服器端的指令碼解譯器。那麼對於它來說,效率和資源佔用率是一個很重要的衡量標準,也就是說,PHP必須盡量介紹記憶體佔用率,考慮下面這段代碼:

 
  1. <?php
  2.    $var = "laruence";
  3.    $var_dup = $var;
  4.    unset($var);
  5. ?>

第一行代碼建立了一個字串變數,申請了一個大小為9位元組的記憶體,儲存了字串”laruence”和一個NULL(/0)的結尾。
第二行定義了一個新的字串變數,並將變數var的值”複製”給這個新的變數。
第三行unset了變數var

這樣的代碼在我們平時的指令碼中是很常見的,如果PHP對於每一個變數賦值都重新分配記憶體,copy資料的話,那麼上面的這段代碼公要申請18個位元組的記憶體空間,而我們也很容易的看出來,上面的代碼其實根本沒有必要申請倆份空間,呵呵,PHP的開發人員也看出來了:

我們之前講過,PHP中的變數是用一個儲存在symbol_table中的符號名,對應一個zval來實現的,比如對於上面的第一行代碼,會在symbol_table中儲存一個值”var”, 對應的有一個指標指向一個zval結構,變數值”laruence”儲存在這個zval中,所以不難想象,對於上面的代碼來說,我們完全可以讓”var”和”var_dup”對應的指標都指向同一個zval就可以了。

PHP也是這樣做的,這個時候就需要介紹我們之前一直沒有介紹過的zval結構中的refcount欄位了。
refcount,顧名思義,記錄了當前的zval被引用的計數。
比如對於代碼:

 
  1. <?php
  2.    $var = 1;
  3.    $var_dup = $var;
  4. ?>

第一行,建立了一個整形變數,變數值是1。 此時儲存整形1的這個zval的refcount為1。
第二行,建立了一個新的整形變數,變數也指向剛才建立的zval,並將這個zval的refcount加1,此時這個zval的refcount為2。
PHP提供了一個函數可以協助我們瞭解這個過程debug_zval_dump:

 
  1. <?php
  2.  $var = 1;
  3.  debug_zval_dump($var);
  4.  $var_dup = $var;
  5.  debug_zval_dump($var);
  6. ?>

輸出:

 
  1. long(1) refcount(2)
  2. long(1) refcount(3

如果你奇怪 ,var的refcount應該是1啊?
我們知道,對於簡單變數,PHP是以傳值的形式穿參數的。也就是說,當執行debug_zval_dump($var)的時候,$var會以傳值的方式傳遞給debug_zval_dump,也就是會導致var的refcount加1,所以我們只要能看到,當變數賦值給一個變數以後,能導致zval的refcount加1這個事實即可。

現在我們回頭看文章開頭的代碼, 當執行了最後一行unset($var)以後,會發生什麼呢? 對,既是refcount減1,上代碼:

 
  1. <?php
  2.    $var = "laruence";
  3.    $var_dup = $var;
  4.    unset($var);
  5.    debug_zval_dump($var_dup);
  6. ?>

輸出:

 
  1. string(8) "laruence" refcount(2

但是,對於下面的代碼呢?

 
  1. <?php
  2.    $var = "laruence";
  3.    $var_dup = $var;
  4.    $var = 1;
  5. ?>

很明顯在這段代碼執行以後,$var_dup的值應該還是”laruence”, 那麼這又是怎麼實現的呢?
這就是PHP的copy on write機制:
PHP在修改一個變數以前,會首先查看這個變數的refcount,如果refcount大於1,PHP就會執行一個分離的常式, 對於上面的代碼,當執行到第三行的時候,PHP發現$var指向的zval的refcount大於1,那麼PHP就會複製一個新的zval出來,將原zval的refcount減1,並修改symbol_table,使得$var和$var_dup分離(Separation)。這個機制就是所謂的copy on write(寫時複製)。

上代碼測試:

 
  1. <?php
  2.    $var = "laruence";
  3.    $var_dup = $var;
  4.    $var = 1;
  5.    debug_zval_dump($var);
  6.    debug_zval_dump($var_dup);
  7. ?>

輸出:

 
  1. long(1) refcount(2)
  2. string(8) "laruence" refcount(2

現在我們知道,當使用變數複製的時候 ,PHP內部並不是真正的複製,而是採用指向相同的結構來盡量節約開銷。那麼,對於PHP中的引用,那又是如何?呢?

 
  1. <?php
  2.    $var = "laruence";
  3.    $var_ref = &$var;
  4.    $var_ref = 1;
  5. ?>

這段代碼結束以後,$var也會被間接的修改為1,這個過程稱作(change on write:寫時改變)。那麼ZE是怎麼知道,這次的複製是不需要Separation的呢?
這個時候就要用到zval中的is_ref欄位了:
對於上面的代碼,當第二行執行以後,$var所代表的zval的refcount變為2,並且同時置is_ref為1。
到第三行的時候,PHP先檢查var_ref代表的zval的is_ref欄位,如果為1,則不分離,大體邏輯示意如下:

 
  1.  if((*val)->is_ref || (*val)->refcount<2){
  2.           //不執行Separation
  3.         ... ;//process
  4.   
    1. }

但是,問題又來了,對於如下的代碼,又會怎樣呢?

 
  1. <?php
  2.    $var = "laruence";
  3.    $var_dup = $var;
  4.    $var_ref = &$var;
  5. ?>

對於上面的代碼,存在一對copy on write的變數$var和$var_dup, 又有一對change on write機制的變數對$var和$var_ref,這個情況又是如何運作的呢?

當第二行執行的時候,和前面講過的一樣,$var_dup 和 $var 指向相同的zval, refcount為2.
當執行第三行的時候,PHP發現要操作的zval的refcount大於1,則,PHP會執行Separation, 將$var_dup分離出去,並將$var和$var_ref做change on write關聯。也就是,refcount=2, is_ref=1;

基於這樣的分析,我們就可以讓debug_zval_dump出refcount為1的結果來:

 
  1. <?php
  2.      $var = "laruence";
  3.     $var_dup = &$var;
  4.      debug_zval_dump($var);
  5. ?>

輸出:

 
  1. string(8) "laruence" refcount(1

詳細原因,讀者你只要稍加分析就能得出,我就不越俎代庖了。;)

這次我們介紹了PHP的變數分離機制,下次我會繼續介紹如果在擴充中接收和傳出PHP指令碼中的參數。

  • 原文地址: http://www.laruence.com/2008/09/19/520.html
  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.