美文网首页
PHP引用计数基础

PHP引用计数基础

作者: 炸茄盒 | 来源:发表于2017-02-21 16:33 被阅读0次

    在zval中存储了两个重要字段,is_ref和refcount。

    is_ref为true时,表示变量是引用变量,否则为普通变量。

    refcount表示,变量的引用次数。

    通过xdebug_debug_zval可以查看“refcount”和“is_ref”的值。

    一般数据类型


    一般数据类型的行为较为简单,单独一个变量的is_ref为false,refcount为1。

    如:

    $a = "new string";

    xdebug_debug_zval('a');

    会输出:

    a: (refcount=1, is_ref=0)='new string'

    当赋值给新的变量时,如果是普通赋值,则refcount++

    如:

    $a = "new string";

    $b = $a;

    xdebug_debug_zval( 'a' );

    会输出:

    a: (refcount=2, is_ref=0)='new string'

    如果是引用赋值,则refcount++,且is_ref会变为true

    如:

    $a = "new string";

    $b = &$a;

    xdebug_debug_zval( 'a' );

    会输出:

    a: (refcount=2, is_ref=1)='new string'

    通过unset()可以减少引用计数

    如:

    $a = "new string";

    $c = $b = $a;

    xdebug_debug_zval( 'a' );

    unset( $b, $c );

    xdebug_debug_zval( 'a' );

    会输出:

    a: (refcount=3, is_ref=0)='new string'

    a: (refcount=1, is_ref=0)='new string'

    如果经过unset()后,refcount的值变为1,则is_ref会变为false

    如:

    $a = "new string";

    $b = &$a;

    xdebug_debug_zval( 'a' );

    unset($b);

    xdebug_debug_zval( 'a' );

    会输出:

    a: (refcount=2, is_ref=1)='new string'

    a: (refcount=1, is_ref=0)='new string'

    普通变量按照copy on write执行,某个变量修改后,会与其他变量分离:

    $a = "new string";

    $b = $a;

    $c = $b;

    xdebug_debug_zval( 'a' );

    xdebug_debug_zval( 'c' );

    $c = "another string";

    xdebug_debug_zval( 'a' );

    xdebug_debug_zval( 'c' );

    会输出:

    a: (refcount=3, is_ref=0)='new string'

    c: (refcount=3, is_ref=0)='new string'

    a: (refcount=2, is_ref=0)='new string'

    c: (refcount=1, is_ref=0)='another string'

    引用变量按照change on write执行,某个变量改变后,其余变量也会相应改变

    如:

    $a = "new string";

    $b = &$a;

    $c = &$b;

    xdebug_debug_zval( 'a' );

    xdebug_debug_zval( 'c' );

    $c = "another string";

    xdebug_debug_zval( 'a' );

    xdebug_debug_zval( 'c' );

    会输出:

    a: (refcount=3, is_ref=1)='new string'

    c: (refcount=3, is_ref=1)='new string'

    a: (refcount=3, is_ref=1)='another string'

    c: (refcount=3, is_ref=1)='another string'

    普通变量变为引用变量也会被当作值的变化,会引发copy on write。

    如:

    $a = 1;

    $b = $a;

    $c = &$b;

    xdebug_debug_zval('a');

    xdebug_debug_zval('b');

    xdebug_debug_zval('c');

    会输出:

    a: (refcount=1, is_ref=0)=1

    b: (refcount=2, is_ref=1)=1

    c: (refcount=2, is_ref=1)=1

    如:

    $a = 1;

    $b = &$a;

    $c = $b;

    xdebug_debug_zval('a');

    xdebug_debug_zval('b');

    xdebug_debug_zval('c');

    会输出:

    a: (refcount=2, is_ref=1)=1

    b: (refcount=2, is_ref=1)=1

    c: (refcount=1, is_ref=0)=1

    数组引用


    如:

    $a = array( 'meaning' => 'life', 'number' => 42 );

    xdebug_debug_zval( 'a' );

    会输出:

    a: (refcount=1, is_ref=0)=array (

    'meaning' => (refcount=1, is_ref=0)='life',

    'number' => (refcount=1, is_ref=0)=42

    )

    如:

    $a = array( 'meaning' => 'life', 'number' => 42 );

    $a['life'] = $a['meaning'];

    xdebug_debug_zval( 'a' );

    会输出:

    a: (refcount=1, is_ref=0)=array (

    'meaning' => (refcount=2, is_ref=0)='life',

    'number' => (refcount=1, is_ref=0)=42,

    'life' => (refcount=2, is_ref=0)='life'

    )

    对于循环引用:

    $a = array( 'one' );

    $a[] =& $a;

    xdebug_debug_zval( 'a' );

    输出:

    a: (refcount=2, is_ref=1)=array (

    0 => (refcount=1, is_ref=0)='one',

    1 => (refcount=2, is_ref=1)=...

    )

    对象


    // $a 是指向 Foo object 0 的一个指针p0

    $a = new Foo;

    // $b 是指向 Foo object 0 的另一个指针p1

    $b = $a; 

    // $c 和 $a 是p0的引用

    $c = &$a; 

    // $a 和 $c 是p0的引用,但是p0改为指向Foo object 1, $b 不变

    $a = new Foo; 

    // $c 是指向 Foo object 1的指针p0

    unset($a); 

    // $a 和 $b 是p1的引用

    $a = &$b; 

    // p1 变为 NULL,$a 和 $b 都是引用. Foo object 0 可以被GC

    $a = NULL;

    // $b 不存在了, $a 是p1,值为 NULL

    unset($b); 

    // $a 是指向 Foo object 2的指针p1 , $c是指向 Foo object 1的指针p0

    $a = clone $c; 

    // Foo object 1可以被gc.

    unset($c); 

    // $c 是指向Foo object 2的指针p2,$a 是指向 Foo object 2的指针p1

    $c = $a; 

    // $c 是p2, 仍然指向Foo object 2

    unset($a); 

    // $a 和 $c 都是p2的引用

    $a = &$c; 

    const ABC = TRUE;

    if(ABC) {

    // Foo object 2 可以被gc

        $a = NULL;

    } else {

    // $c 仍然指向Foo object 2

        unset($a); 

    }

    总结


    1. is_ref表示变量是否为引用变量,refcount表示变量引用次数

    2. 普通变量被其他变量引用时,变成is_ref变为true

    3. 经过unset()后,refcount变成1时,is_ref会被置为false

    4. 普通变量按照copy on write执行,某个变量的值改变时,会单独复制一份,再改变值

    5. 引用变量按照change on write执行,某个变量的值改变时,其余相关的值也会改变

    6. 数组里的每一个元素都按照上述原则执行

    7. 对象可以看作一个指针,指向具体对象,同样按照上述原则执行

    相关文章

      网友评论

          本文标题:PHP引用计数基础

          本文链接:https://www.haomeiwen.com/subject/yjiswttx.html