zl程序教程

您现在的位置是:首页 >  后端

当前栏目

深入解析PHP的引用计数机制

PHP 深入 解析 机制 引用 计数
2023-06-13 09:15:01 时间

PHP的变量声明并赋值后,变量名存在符号表中,而值和类信息存在zval中,zval中包含四个变量,is_ref,refcount,value,type,zval源码如下

复制代码代码如下:

struct_zval_struct{ 
   /*Variableinformation*/ 
   zvalue_valuevalue;    /*value*/ 
   zend_uintrefcount__gc; 
   zend_uchartype;   /*activetype*/ 
   zend_ucharis_ref__gc; 
};

refcount表示value地址与其相同的zval共有多少个,refcount=0时,zval被销毁
is_ref表示一个zval是否被引用,有“0”和“1”两种状态

此处分析一下什么时候zval会被复制或者开辟新的内存空间呢
1.当is_ref=0,且refcount>1时,如果改变某个指向该zval的变量的值,会生成新zval,原zval的refcount--,例如:$a=1;$b=$a;$b=2;,zval将被复制,也就是说原先ab指向同一个zval,后来b会使用新开辟的zval

2.当is_ref=0,且refcount>1时,如果将zval赋值给某个引用变量,那么用来赋值和变量和被赋值的变量会使用同一个原zval,而其他指向原zval的变量将会指向一个新复制的zval,且refcount会被重新计算,例如:$a=1;$b=$a;$c=$a;$d=&$a;,此时ad使用原zval,bc使用新复制出来的zval

3.当is_ref=1,且refcount>1时,如果将zval复制给某个非引用变量,该非引用变量会使用一个新复制的zval,元zval的refcount不变,例如:$a=1;$b=&$a;$c=$a,那么ab使用原zval,而c使用新复制的zval
type表示该zval的值类型,宏定义如下

复制代码代码如下:

#defineIS_NULL    0 
#defineIS_LONG    1 
#defineIS_DOUBLE  2 
#defineIS_BOOL    3 
#defineIS_ARRAY   4 
#defineIS_OBJECT  5 
#defineIS_STRING  6 
#defineIS_RESOURCE7 
#defineIS_CONSTANT8 
#defineIS_CONSTANT_ARRAY  9

value表示该zval的值,他也是个共同体,代码如下
复制代码代码如下:
typedefunion_zvalue_value{ 
   longlval;                 /*longvalue*/ 
   doubledval;               /*doublevalue*/ 
   struct{ 
       char*val; 
       intlen; 
   }str; 
   HashTable*ht;             /*hashtablevalue*/ 
   zend_object_valueobj; 
}zvalue_value;

现在你知道php是如何类型变换的了,因为他的值存的其实是个可以代表任何类型的结构体,而具体的取值则根据type来决定是用共同体里的哪个变量来存值的

见下面的例子1
复制代码代码如下:
.-----------
$a=1;
$b=$a;
$c=$a;
.-----------
$d=&$a;
.-----------
$a=2;
.-----------
$b=null;

查看refcount,is_ref,zval的变化
执行完第一部分后来看看输出
1-----------------------------
a:(refcount=3,is_ref=0),int1
b:(refcount=3,is_ref=0),int1
c:(refcount=3,is_ref=0),int1
可以看出来a,b,c使用同一个zval
再看执行完第二部分的
2----------------------------
a:(refcount=2,is_ref=1),int1
b:(refcount=2,is_ref=0),int1
c:(refcount=2,is_ref=0),int1
d:(refcount=2,is_ref=1),int1
注意此时a,d在一起了,他们使用同一个zval,而bc使用一个新生成的zval,同时重新计算两个zval的refcount和is_ref
3----------------------------
a:(refcount=2,is_ref=1),int2
b:(refcount=2,is_ref=0),int1
c:(refcount=2,is_ref=0),int1
d:(refcount=2,is_ref=1),int2
可以知道ad这两个is_ref=1的好基友的值是同时改变的
4----------------------------
a:(refcount=2,is_ref=1),int2
b:(refcount=1,is_ref=0),null
c:(refcount=1,is_ref=0),int1
d:(refcount=2,is_ref=1),int2
bc由于他们的zval的is_ref=0,所以他们不是好基友,他们的值不会同时改变,于是bc的zval再次分裂,b=nullc=1