最近看PHP中的引用計數器部分,首先被各種繞暈,然後通過看博客和分析後,總結了一個比較通俗的解釋,能幫助自己很好地記憶,也希望能幫助到各位讀者。這裡分享一遍博文,是比較正統的解釋:PHP變量之引用(http://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),那還需考察右側是否是獨居,若是獨居,則 左右兩個變量共用右側變量空間,否則右側變量從原來的地方分離出來和左側開辟新空間。 只要是引用賦值,那麼左側變量總是要與之前結合的或共用的變量進行分離。