在zval中存儲了兩個重要字段,is_ref和refcount。
is_ref為true時,表示變量是引用變量,否則為普通變量。
refcount表示,變量的引用次數(shù)。
通過xdebug_debug_zval可以查看“refcount”和“is_ref”的值。
一般數(shù)據(jù)類型
一般數(shù)據(jù)類型的行為較為簡單,單獨一個變量的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會變?yōu)閠rue
如:
$a = "new string";
$b = &$a;
xdebug_debug_zval( 'a' );
會輸出:
a: (refcount=2, is_ref=1)='new string'
通過unset()可以減少引用計數(shù)
如:
$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'
如果經(jīng)過unset()后,refcount的值變?yōu)?,則is_ref會變?yōu)閒alse
如:
$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執(zhí)行,某個變量修改后,會與其他變量分離:
$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執(zhí)行,某個變量改變后,其余變量也會相應改變
如:
$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'
普通變量變?yōu)橐米兞恳矔划斪髦档淖兓?,會引發(fā)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
數(shù)組引用
如:
$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'
)
對于循環(huán)引用:
$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 變?yōu)?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);?
}
總結(jié)
1. is_ref表示變量是否為引用變量,refcount表示變量引用次數(shù)
2. 普通變量被其他變量引用時,變成is_ref變?yōu)閠rue
3. 經(jīng)過unset()后,refcount變成1時,is_ref會被置為false
4. 普通變量按照copy on write執(zhí)行,某個變量的值改變時,會單獨復制一份,再改變值
5. 引用變量按照change on write執(zhí)行,某個變量的值改變時,其余相關的值也會改變
6. 數(shù)組里的每一個元素都按照上述原則執(zhí)行
7. 對象可以看作一個指針,指向具體對象,同樣按照上述原則執(zhí)行