如何在PHP赋值中运行的详细说明
前言
在PHP中,一个变量赋值时,内部经历了怎样的逻辑判断?
PHP通过zval在内核中存储变量,zval是在Zend/zend.h文件中定义的
struct _ zval _ struct { zvalue _ value;/*变量*/Zend _ uint ref count _ GC的值;zend_uchar类型;/*变量*/zend_uchar的当前数据类型是_ ref _ GC;};typedef struct _ zval _ struct zval//在Zend/zend_types.h中定义:typedef无符号int zend _ uinttypedef无符号字符zend _ uchar使用xdebug的xdebug_debug_zval函数打印出变量的refcount和is _ ref值。
$ a=' Hello World$ b=$ a;以上内容在内核中是如何执行的?
zval * hellovalMAKE _ STD _ ZVAL(hello val);ZVAL_STRING(helloval,‘Hello World’,1);Zend _ hash _ add(EG(active _ symbol _ table),' a ',sizeof('a '),helloval,sizeof(zval*),NULL);ZVAL _ ADDREF(hello val);//这句话很特别。我们显式添加helloal结构的refcountzend _ hash _ add(如(active _ symbol _ table)、' b '、sizeof ('b ')、helloal、sizeof (zval *)、null)。可以看出,当变量赋值时,两个变量实际上指向同一个地址空间。那么问题来了。如果指向同一个地址空间,就不是修改A,B也会相应改变。这涉及到php的写时复制机制。对于上述代码,如果下面一行$b='123 ',判断过程如下:
如果这个变量的zval部分的refcount小于2,这意味着没有其他变量在使用,直接修改这个值;否则,复制一个zval值,减少原始zval的引用计数,初始化新zval的引用计数,并修改新复制的简单zval变量
先参考赋值,再普通赋值
var _ dump(memory _ get _ usage());$ a=' 1234567890xdebug _ debug _ zval(' a ');var _ dump(memory _ get _ usage());$ b=$ a;xdebug_debug_zval('a ',' b ');var _ dump(memory _ get _ usage());$ c=$ a;xdebug_debug_zval('a ',' b ',' c ');var _ dump(memory _ get _ usage());$ a=' 1234567890var _ dump(memory _ get _ usage());$ b=$ a;var _ dump(memory _ get _ usage());$ c=$ a;输出如下:
int(121672)a: (refcount=1,is_ref=0)='1234567890 '
int(121776)a: (refcount=2,is_ref=1)='1234567890 ' b :(ref count=2,is _ ref=1)=' 1234567890 '
int(121824)a: (refcount=2,is _ ref=1)=' 1234567890 ' b :(ref count=2,is _ ref=1)=' 1234567890 ' c :(ref count=1,is_ref=0)='1234567890 '
int(121928)
$a的赋值打开了一个104字节的空间,变量为arefcount=1和is _ ref=0
$b的赋值打开了一个48字节的空间,变量是fcount=2和is _ ref=1。48字节被符号表占用,A和B执行相同的地址空间
$c的赋值打开了一个104字节的空间。因为A和B是引用,当C赋值时,会打开一个新的空间,复制一个zval内容,并初始化refcount,就是is_ref,所以A的refcount不变,C的refcount为=1
一般作业后接参考作业
var _ dump(memory _ get _ usage());$ a=' 1234567890xdebug _ debug _ zval(' a ');var _ dump(memory _ get _ usage());$ b=$ a;xdebug_debug_zval('a ',' b ');var _ dump(memory _ get _ usage());$ c=$ a;xdebug_debug_zval('a ',' b ',' c ');var _ dump(memory _ get _ usage());输出如下:
int(121672)
a: (refcount=1,is _ ref=0)=' 1234567890 ' int(121776)
a: (refcount=2,is _ ref=0)=' 1234567890 ' b :(ref count=2,is _ ref=0)=' 1234567890 ' int(121824)
a: (refcount=2,is _ ref=1)=' 1234567890 ' b :(ref count=1,is _ ref=0)=' 1234567890 ' c :(ref count=2,is _ ref=1)=' 1234567890 ' int(121928)
$a的赋值打开了一个104字节的空间,变量为arefcount=1和is _ ref=0
$b的赋值打开了一个48字节的空间,变量是fcount=2和is _ ref=1。48字节被符号表占用,A和B指向同一个地址空间
$c的赋值打开了一个104字节的空间。由于A和C是引用,需要与B隔离,因此原始的zval将被赋值、初始化,A和C将指向新复制的zval,而原始的zval则重新计数-1
排列
$ arr=[0=' one '];xdebug _ debug _ zval(' arr ');$ arr[1]=$ arr;xdebug _ debug _ zval(' arr ');$ arr[2]=$ arr;xdebug _ debug _ zval(' arr ');unset($ arr[1]);xdebug _ debug _ zval(' arr ');unset($ arr[2]);xdebug _ debug _ zval(' arr ');输出如下:
arr: (refcount=1,is_ref=0)=(数组0=(ref count=1,is_ref=0)='one ')数组: (refcount=1,is_ref=0)=(数组0=(ref count=2,is_ref=0)=' one ',1=(refcount=1,is _ ref=0)=(数组0=(ref count=2,is _ ref=0)=' one ')数组:(ref count=1,is _ ref=0).))arr: (refcount=1,is_ref=0)=array (0=(refcount=3,is_ref=0)='one ',2=(refcount=1,is_ref=0)=array (0=(refcount=3,is_ref=0)='one ',1=(refcount=1,is_ref=0)=array(.)))arr: (refcount=1,is_ref=0)=array (0=(refcount=1,is _ ref=0)=' one ')$ arr=[0=' one '];xdebug _ debug _ zval(' arr ');$ arr[1]=$ arr;xdebug _ debug _ zval(' arr ');$ arr[2]=$ arr;xdebug _ debug _ zval(' arr ');unset($ arr[1]);xdebug _ debug _ zval(' arr ');unset($ arr[2]);xdebug _ debug _ zval(' arr ');
输出内容如下:
arr: (refcount=1,is_ref=0)=array (0=(refcount=1,is_ref=0)='one ')arr :(ref count=2,is_ref=1)=array (0=(refcount=1,is _ ref=0)=' one ',1=(refcount=2,is_ref=1)=.)arr: (refcount=3,is_ref=1)=array (0=(refcount=2,is_ref=0)='one ',1=(refcount=3,is_ref=1)=.2=(refcount=2,is_ref=0)=array (0=(refcount=2,is_ref=0)='one ',1=(refcount=3,is_ref=1)=.2=(refcount=2,is_ref=0)=.))arr: (refcount=2,is_ref=1)=array (0=(refcount=2,is_ref=0)='one ',2=(refcount=2,is_ref=0)=array (0=(refcount=2,is_ref=0)='one ',1=(refcount=2,is_ref=1)=.2=(refcount=2,is_ref=0)=.))arr: (refcount=2,is_ref=1)=array (0=(refcount=2,is_ref=0)='one ')
上面段测试代码很相似,差别只在arr[1]是否是引用赋值。
arr[1]非引用赋值的情况,arr[0]的refcount=赋值次数1,执行两次复原之后,arr,arr[0]的引用计数都跟开始定义的时候一致逮捕[1]引用赋值的情况,arr[0]的refcount=非引用赋值次数1,执行两次复原之后,arr,arr[0]的引用计数都无法回到定义的时候的值。
主要原因在于arr[1]引用赋值,构成一个递归操作。但是如果,至于这个refcount,真的说不明白。当没有arr[2]赋值的时候,执行取消设置,重新计数能回到1 。从下面这张图更加清晰看出内部递归引用
当出现上面这种情况,refcount本该=1,但实际上面没有被设置为1,这种情况就会出现内存泄漏。上面代码循环执行100次,内存从一开始121096 上升到169224,内存占用上升了5k。
对象
$user=新用户();$ m=$ user $ user-user=' ';$ user-name=' sdfsdfsxdebug_debug_zval('user ',' m ');以上内容输出
(refcount=2,is_ref=0)=类用户{ public $name=(refcount=1,is _ ref=0)=' sdfsdfs ';public $model=(refcount=1,is _ ref=0)=NULL;public $user=(refcount=1,is_ref=0)='' }m: (refcount=2,is_ref=0)=类用户{ public $name=(refcount=1,is _ ref=0)=' sdfsdfs ';public $model=(refcount=1,is _ ref=0)=NULL;public $user=(refcount=1,is_ref=0)='' }
程序给出的is_ref=0。引用计数与普通变量一直。但是类的赋值是引用赋值。
$user=新用户();$ user-user=$ user;$ user-name=' sdfsdfsxdebug _ debug _ zval(' user ');unset($ user);上面内容输出:
user: (refcount=2,is_ref=0)=类用户{ public $name=(refcount=1,is _ ref=0)=' sdfsdfs ';public $user=(refcount=2,is_ref=0)=.}
这里由于类的赋值是引用赋值,索引也构成了一个递归操作,这样也会跟数组一样出现内存泄漏的情况。对以下代码个自行100次
$user=新用户();$ user-user=$ user;$ user-name=' sdfsdfsxdebug _ debug _ zval(' user ');unset($ user);$user=新用户();$user-user=新订单();$ user-name=' sdfsdfsxdebug _ debug _ zval(' user ');unset($ user);第一段代码前后内存差1408字节。第二段代码差208字节。
总结
以上就是本文的全部内容。希望本文的内容对大家的学习或工作有一定的参考价值。有问题可以留言交流。谢谢你的支持。
版权声明:如何在PHP赋值中运行的详细说明是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。