前言
在php中,一个变量被赋值,内部到底经历了怎样的逻辑判断呢?
php在内核中是通过zval这个结构体来存储变量的,它的定义在zend/zend.h文件里
struct _zval_struct {zvalue_value value; /* 变量的值 */zend_uint refcount__gc;zend_uchar type; /* 变量当前的数据类型 */zend_uchar is_ref__gc;};typedef struct _zval_struct zval;//在zend/zend_types.h里定义的:typedef unsigned int zend_uint;typedef unsigned char zend_uchar;
使用xdebug的xdebug_debug_zval函数可以打印出变量的refcount,is_ref的值夸夸我的老师。
$a = 'hello world';$b = $a;
以上内容在内核中怎么执行呢?
zval *helloval;make_std_zval(helloval);zval_string(helloval, "hello world", 1);zend_hash_add(eg(active_symbol_table), "a", sizeof("a"),&helloval, sizeof(zval*), null);zval_addref(helloval); //这句很特殊,我们显式的增加了helloval结构体的refcountzend_hash_add(eg(active_symbol_table), "b", sizeof("b"),&helloval, sizeof(zval*), null);
可以看出来,当变量赋值的时候,其实两个变量指向的是同一个地址空间。那么问题来了,如果指向同一个地址空间,那不是修改a,b也会跟着改变。这就涉及php的 写时复制机制 。 以上代码,如果后面一行为 $b = ‘123’ 判断过程如下:
如果这个变量的zval部分的refcount小于2,代表没有别的变量在用,则直接修改这个值否则,复制一份zval 的值,减少原zval的refcount的值,初始化新的zval的refcount,修改新复制的zval简单变量
先引用赋值后普通赋值
var_dump(memory_get_usage());$a = '1234567890';xdebug_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 = '1234567890';var_dump(memory_get_usage());$b = &am等温线图的判读p;$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: (refcount=2, is_ref=1)=’1234567890′int(121824)
a: (refcount=2, is_ref=1)=’1234567890′
b: (refcount=2, is_ref=1)=’1234567890′
c: (refcount=1, is_ref=0)=’1234567890′int(121928)
$a 赋值,开辟了104byte空间,变量a refcount=1,is_ref=0
$b 赋值,开辟了48byte空间,变量a refcount=2,is_ref=1。48byte是符号表占用,a,b执行同一个地址空间
$c 赋值,开辟了104byte空间。由于a,b是引用,所以在c赋值的时候,会开辟新空间,复制a zval内容,并初始化refcount,is_ref,所以a 的refcount不变,c 的refcount=1
先普通赋值后引用赋值
var_dump(memory_get_usage());$a = '1234567890';xdebug_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: (refcount=2, is_ref=0)=’1234567890′
int(121824)a: (refcount=2, is_ref=1)=’1234567890′
b: (refcount=1, is_ref=0)=’1234567890′
c: (refcount=2, is_ref=1)=’1234567890′
int(121928)
$a 赋值,开辟了104byte空间,变量a refcount=1,is_ref=0
$b 赋值,开辟了48byte空间,变量a refcount=2,is_ref=1。48byte是符号表占用,a,b指向同一个地址空间
$c 赋值,开辟了104byte空间。由于a,c是引用,需要与b隔离开来,因此会赋值原有的zval,初始化zval,将a,c指向新复制的zval,同时原有的zval refcount-1
数组
$arr = [0=>'one'];xdebug_debug_zval('arr');$arr[1] = $arr;xdebug_debug_zval('arr');$arr[2] = $arr;xdebug_debug_zval('arr');unt($arr[1]);xdebug_debug_zval('arr');unt($arr[2]);xdebug_debug_zval('arr');
输出内容如下:
arr: (refcount=1, is_ref=0)=array ( 0 => (refcount=1, is_ref=0)=’one’)
)
arr: (refcount=1, is_ref=0)=array (
0 => (refcount=2, is_ref=0)=’one’,
1 => (refcount=1, is_ref=0)=array (
0 => (refcount=2, is_ref=0)=’one’
)
)
arr: (refcount=1, is_ref=0)=array (
0 => (refcount=3, is_ref=0)=’one’,
1 => (refcount=2, 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=2, is_ref=0)=array (…)
)
)
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̵宣纸7;);$arr[2] = $arr;xdebug_debug_zval(‘arr’);unt($arr[1]);xdebug_debug_zval(‘arr’);unt($arr[2]);xdebug_debug_zval(‘arr’);
输出内容如下:
arr: (refcount=1, is_ref=0)=array (
0 => (refcount=1, is_ref=0)=’one’
)
arr: (refcount=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 => (refcoun90年代经典流行歌曲t=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,执行两次unt之后,arr,arr[0]的refcount都跟开始定义的时候一致。 arr[1]引用赋值的情况,arr[0]的refcount = 非引用赋值次数+1,执行两次unt之后,arr,arr[0] 的refcount都无法回到定义的时候的值。
主要原因在于arr[1]引用赋值,构成一个递归操作。 但是如果,至于这个refcount,真的说不明白。当没有arr[2]赋值的时候,执行unt, arr refcount能回到1 。从下面这张图更加清晰看出内部递归引用
当出现上面这种情况,refcount本该=1,但实际上面没有被设置为1,这种情况就会出现内存泄漏。上面代码循环执行100次,内存从一开始121096 上升到169224,内存占用上升了5k 。
对象
$ur = new ur(); $m = $ur; $ur->ur =''; $ur->name = 'sdfsdfs'; xdebug_debug_zval('ur','m');
以上内容输出
(refcount=2, is_ref=0)=class ur {
public $name = (refcount=1, is_ref=0)=’sdfsdfs’;
public $model = (refcount=1, is_ref=0)=null;
public $ur = (refcount=1, is_ref=0)=”
}
m: (refcount=2, is_ref=0)=class ur {
public $name = (refcount=1, is_ref=0)=’sdfsdfs’;
public $model = (refcount=1, is_ref=0)=null;
public $ur = (refcount=1, is_ref=0)=”
}
xdebug给出的is_ref=0。refcount与普通变量一直。但是类的赋值是引用赋值。
$ur = new ur(); $ur->ur = $ur; $ur->name = 'sdfsdfs'; xdebug_debug_zval('ur'); unt($ur);
上面内容输出:
ur: (refcount=2, is_ref=0)=class ur { public $name = (refcount=1, is_ref=0)=’sdfsdfs’; public $ur = (refcount=2, is_ref=0)=… }
这里由于类的赋值是引用赋值,索引也构成了一个递归操作,这样也会跟数组一样出现内存泄漏的情况。对以下代码个自行100次
$ur = new ur(); $ur->ur = $ur; $ur->name = 'sdfsdfs'; xdebug_debug_zval('ur'); unt($ur);$ur = new ur(); $ur->ur = new order(); $ur->name = 'sdfsdfs'; xdebug_debug_zval('ur'); unt($ur);
第一段代码前后内存差1408 byte. 第二段代码差208 byte。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工盲肠炎作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对www.887551.com的支持。
本文发布于:2023-04-06 16:31:04,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/81fff8bb0501df2dc953a1a9d97297a5.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:PHP赋值的内部是如何跑的详解.doc
本文 PDF 下载地址:PHP赋值的内部是如何跑的详解.pdf
留言与评论(共有 0 条评论) |