PHP 引用計數器 通俗版解釋

概述

最近看PHP中的引用計數器部分,首先被各種繞暈,然後通過看博客和分析後,總結瞭一個比較通俗的解釋,能幫助自己很好地記憶,也希望能幫助到各位讀者。這裡分享一遍博文,是比較正統的解釋:PHP變量之引用(https://hilojack.sinaapp.com/?p=1392)。

建議

研究PHP引用計數器的變化可以通過安裝Xdebug擴展來學習,安裝後直接調用 xdebug_debug_zval(‘var’) 來看變量 $var的引用計數器情況。

基礎知識

談引用計數器需要對PHP中變量的存儲、引用計數的機制有所瞭解,引用計數當然是節約內存,在不影響語義正確性的前提下,讓多個變量符號共享一個內存值空間(又稱為變量容器)。引用計數什麼時候發生變化:賦值。 賦值又有兩種: 值傳遞賦值和引用賦值。比較難以理解的是引用賦值。引用計數的另一個作用就是指示什麼時候可以共用同一空間,什麼時候必須進行變量分離(另開辟空間)。

通俗解釋

為瞭便於理解和記憶,本人對賦值中的各種情景給以通俗的解釋,不要與現實情況對號入座,大笑

& 號是結合,可以相當於結婚,但是PHP中允許多個人一起結婚(即一夫多妻制或一妻多夫制),這個比較變態。 註意結婚必須同居,分居是不行的。除瞭結婚外還有一種形態叫 合租。合租也是同居,但是沒有任何關系,允許多人合租,這個是合情合理的。當然還有一種情況是獨居,這個比較容易理解。以下面的例子說明賦值語句與這三種狀態對應關系:

情景一:

$a = "a";  // $a 獨居, is_ref = 0, refcount = 1;
$b = $a;   // $a 與 $b 合租, is_ref = 0, refcount = 2;

情景二:

$a = "a"; // $a 獨居, is_ref = 0, refcount = 1;
$b = &$a; // $a 與 $b 結婚, is_ref = 1, refcount = 2;

上面可以看出, is_ref可以理解為結婚證,=1 表示兩個和多個變量是結婚關系, =0沒有結婚(可以合租或同居),refcount表示瞭多少個變量住在一起瞭,=1表示獨居,>1表示多人同居(記住,結婚必須同居,但同居未必是結婚關系)。

下面開始分析賦值時的變量變化關系:

情景一:

$a = "a";  // $a 獨居, is_ref = 0, refcount = 1;
$b = $a;   // $a 與 $b 合租, is_ref = 0, refcount = 2;


$va = "b"; // $va 獨居, is_ref = 0, refcount = 1;
$vb = $va; // $va 和 $vb 合租, is_ref = 0, refcount = 2;


$a = $va;  // $a是單身,$va也是單身,因此 $a 搬去與 $va 同居,現在$a,$va,$vb三人同居
賦值後: 
$a: is_ref = 0, refcount = 3, string = "xyz"
$b: is_ref = 0, refcount = 1, string = "qwe"
$va: is_ref = 0, refcount = 3, string "xyz"
$vb: is_ref = 0, refcount = 3, string "xyz"

情景二:

$a = "a";  // $a 獨居, is_ref = 0, refcount = 1;
$b = &$a;   // $a 與 $b 結婚, is_ref = 1, refcount = 2;


$va = "b"; // $va 獨居, is_ref = 0, refcount = 1;
$vb = $va; // $va 和 $vb 合租, is_ref = 0, refcount = 2;


$a = $va;  // $a是已婚, $a 不能隨便單獨搬出去,賦值會使$va的值拷貝給$a,其他關系不變
$a: is_ref = 1, refcount = 2, string = "xyz"
$b: is_ref = 1, refcount = 2, string = "xyz"
$va: is_ref = 0, refcount = 2, string "xyz"
$vb: is_ref = 0, refcount = 2, string "xyz"

情景三:

$a = "a";  // $a 獨居, is_ref = 0, refcount = 1;
$b = &$a;   // $a 與 $b 結婚, is_ref = 1, refcount = 2;


$va = "b"; // $va 獨居, is_ref = 0, refcount = 1;
$vb = &$va; // $va 和 $vb 結婚, is_ref = 1, refcount = 2;


$a = $va;  // $a是已婚, 與情景二相同,值拷貝,關系不變
賦值後:
$a: is_ref = 1, refcount = 2, string = "xyz"
$b: is_ref = 1, refcount = 2, string = "xyz"
$va: is_ref = 1, refcount = 2, string "xyz"
$vb: is_ref = 1, refcount = 2, string "xyz"

情景四:

$a = "qwe";  // $a 獨居, is_ref = 0, refcount = 1;
$b = $a;   // $a 與 $b 同居, is_ref = 0, refcount = 2;

$va = "xyz"; // $va 獨居, is_ref = 0, refcount = 1;
$vb = &$va; // $va 和 $vb 結婚, is_ref = 1, refcount = 2;

$a = $va;  // $a 想與 $va同居(而非結婚),但是$va已婚的,因此 $a隻能從$b那裡搬出來,重新分配個房子,值與$va一樣(術語叫:變量分離);$va和$vb關系不變

賦值後: 
$a: is_ref = 0, refcount = 1, string = "xyz"
$b: is_ref = 0, refcount = 1, string = "qwe"
$va: is_ref = 1, refcount = 2, string "xyz"
$vb: is_ref = 1, refcount = 2, string "xyz"

情景五:

$a = "qwe";  // $a 獨居, is_ref = 0, refcount = 1;
$b = $a;   // $a 與 $b 同居, is_ref = 0, refcount = 2;

$va = "xyz"; // $va 獨居, is_ref = 0, refcount = 1;
$vb = $va; // $va 和 $vb 同居, is_ref = 0, refcount = 2;

$a = &$va;  // $a 想與 $va結婚,現在$a 和 $va 都是單身但是都有室友瞭,因此他們各自從原來的地方搬出來,然後分個新房子,值與$va原來的一樣

賦值後: 
$a: is_ref = 1, refcount = 2, string = "xyz"
$b: is_ref = 0, refcount = 1, string = "qwe"
$va: is_ref = 1, refcount = 2, string "xyz"
$vb: is_ref = 0, refcount = 1, string "xyz"

情景六:

$a = "qwe";  // $a 獨居, is_ref = 0, refcount = 1;
$b = $a;   // $a 與 $b 同居, is_ref = 0, refcount = 2;

$va = "xyz"; // $va 獨居, is_ref = 0, refcount = 1;
$vb = &$va; // $va 和 $vb 結婚, is_ref = 1, refcount = 2;

$a = &$va;  // $a 想與 $va 結婚,但是 $va 是已婚的,而 $a 是單身,因此 $a 搬過去和 $va 住,$va 現在有兩個配偶:$vb 和 $a

賦值後: 
$a: is_ref = 1, refcount = 3, string = "xyz"
$b: is_ref = 0, refcount = 1, string = "qwe"
$va: is_ref = 1, refcount = 3, string "xyz"
$vb: is_ref = 1, refcount = 3, string "xyz"

情景七:

$a = "qwe";  // $a 獨居, is_ref = 0, refcount = 1;
$b = &$a;   // $a 與 $b 結婚, is_ref = 1, refcount = 2;

$va = "xyz"; // $va 獨居, is_ref = 0, refcount = 1;
$vb = $va; // $va 和 $vb 同居, is_ref = 0, refcount = 2;

$a = &$va;  // $a 想與 $va 結婚,但是 $a 是已婚的,$va 是單身, 解決辦法是 $a 離婚後和 $va 結婚,同時 $va 從與 $vb合租的地方搬出來

賦值後: 
$a: is_ref = 1, refcount = 2, string = "xyz"
$b: is_ref = 0, refcount = 1, string = "qwe"
$va: is_ref = 1, refcount = 2, string "xyz"
$vb: is_ref = 0, refcount = 1, string "xyz"

情景八:

$a = "qwe";  // $a 獨居, is_ref = 0, refcount = 1;
$b = &$a;   // $a 與 $b 結婚, is_ref = 1, refcount = 2;

$va = "xyz"; // $va 獨居, is_ref = 0, refcount = 1;
$vb = &$va; // $va 和 $vb 結婚, is_ref = 1, refcount = 2;

$a = &$va;  // $a 想與 $va 結婚,但是 $a和$va都是已婚的,誰離婚?$a!,因為是$a主動想和$va結婚,
// $a 離婚後$va住一起,$va 現在有兩個配偶:$vb 和 $a

賦值後: 
$a: is_ref = 1, refcount = 3, string = "xyz"
$b: is_ref = 0, refcount = 1, string = "qwe"
$va: is_ref = 1, refcount = 3, string "xyz"
$vb: is_ref = 1, refcount = 3, string "xyz"

以上分析瞭簡單變量賦值的各種情形,不包括自引用的情況。概況來講:

簡單賦值 就是同居,需考察等號左側變量的is_ref(即是否已婚),若 is_ref = 1,則值拷貝,否則考慮左側便令,能否在不另外分配內存的情形下,與右側變量共用同一存儲空間(同居),此時要考察右側是否是已婚,若是,則不能同居,變量分離,若右側變量也是單身,則直接共用同一內存,所有同居者都遵循 COW(寫時拷貝)的原則。

引用賦值 就是結合, 需先考察右側變量的引用情況, 若 is_ref = 1,則直接 refcount ++, 出現多人結合的情況, 若右側是非引用的(is_ref = 0),那還需考察右側是否是獨居,若是獨居,則 左右兩個變量共用右側變量空間,否則右側變量從原來的地方分離出來和左側開辟新空間。 隻要是引用賦值,那麼左側變量總是要與之前結合的或共用的變量進行分離。

發佈留言