php5在引入引用計數後,使用了refcount_gc來記錄次數,同時使用is_ref_gc來記錄是不是引用類型。php
例如php7
$a = 'hello';內存
//$a->zval1(type=IS_STRING,refcount_gc=1,is_ref_gc=0)字符串
這個時候$a指向一個結構體,主要看refcount_gc=1,這就是引用計數字段,由於hello這個字符串被賦值給了$a,因此這個時候hello的引用計數就是1string
$b = $a;引用
$b,$a->zval1(type=IS_STRING,refcount_gc=2,is_ref_gc=0)gc
這個時候又把$a賦值給了$b,因此這個時候$b和$a同時指向這個結構體,而且引用計數加1co
$c = &$b;字符
//$a->zval1(type=IS_STRING,refcount_gc=1,is_ref_gc=0)數字
//$c,$b->zval2(type=IS_STRING,refcount_gc=2,is_ref_gc=1)
這個時候由於把$b賦值給了$c,可是是傳址的方式,因此在這個時候就要分離了,$a還指向原來的結構體,$c和$b同時指向一個新的結構體,這個時候zval的is_ref_gc值會改變,使得此時的zval必須進行分離,可是實際上他們的值尚未變化,這使得須要在堆中維護兩個值爲"hello"的zval。
下面咱們來看看php7中的實現
php7引入了新的類型IS_REFERENCE來處理這個問題,首先看看zend_reference的結構體:
struct _zend_reference{
zend_refcounted_h gc;
zval val;
};
$a = 'hello';
//$a->zend_string(refcount=1,val)
$b = $a;
//$b,$a->zend_string(refcount=2,val)
$c = &$b;
//$a->zend_string(refcount=2,val)
//$c,$b->zval(type=IS_REFERENCE,refcount=2)->zend_string(refcount=2,val)
從上面能夠看出來,當使用&操做時,會建立一種新的中間結構體zend_referenct,這個結構體會指向真正的zend_string結構體,因此zend_string結構體的引用計數不變,同時zend_reference結構體的引用計數變爲2,由於$c和$b此時的類型都會變爲zend_reference,這樣的好處是原始的zend_string在內存中始終只有一份(避免了因爲字符串的重複申請致使的內存浪費),更加易於維護。