PHP 引用计数器 通俗版解释

概述

  最近看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,其他<strong>关系</strong>不变
$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),那还需考察右侧是否是独居,若是独居,则 左右两个变量共用右侧变量空间,否则右侧变量从原来的地方分离出来和左侧开辟新空间。 只要是引用赋值,那么左侧变量总是要与之前结合的或共用的变量进行分离。







郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。