php底层原理之变量(二)

5次阅读

共计 2011 个字符,预计需要花费 6 分钟才能阅读完成。

上周我们从底层的角度介绍了 php 变量从生成 -> 常量赋值 -> 销毁的完整生命周期(不了解的同学可以翻看一下前面的文章 php 底层原理之变量(一)),但是我们留了一个思考,不知道大家有答案了没,变量之间的赋值在底层又是如何实现的呢?
变量之间赋值
php 变量的 zval 结构,我们已经介绍了很多遍了,这里我们就不再多作介绍了。但是对于 zval 结构体中的 refcount__gc 和 is_ref__gc 字段我们一直都没有详细介绍过,而这两个字段其实是和变量之间赋值的原理有着密切的关系的。所以,我们这次从几个例子入手,了解这两个字段的变化和由此带来的原理知识
写时复制原理
举例:
$a = “ 许铮的技术成长之路 ”;
$b = $a;
xdebug_debug_zval(“a”, “b”);
结果:
a: (refcount=2, is_ref=0)=’ 许铮的技术成长之路 ’
b: (refcount=2, is_ref=0)=’ 许铮的技术成长之路 ’
看到这里,大家可能会比较蒙。不是变量赋值了么?应该发生值拷贝了呀?怎么两个变量的引用计数不是 1,而是 2 呢?
那是因为,php 在设计的时候,为了节省内存,所以在变量之间赋值时,对于值相同的两个变量,会共用一块内存,也就是会在全局符号表内将变量 b 的变量指针指向变量 a 指向的同一个 zval 结构体,而只有当变量的 zval 结构发生变化时,才会发生变量容器复制的内存变化,也因此叫做写时复制原理
那什么时候会发生写时复制原理呢?
写时复制原理触发时机:php 在修改一个变量时,如果发现变量的 refcount>1,则会执行变量容器的内存复制
举例:
$a = “ 许铮的技术成长之路 ”;
$b = $a; // 此时变量 a 和变量 b 共同指向同一个变量容器,即 refcount>1
$b = “ 许铮的技术成长之路 1 ” // 触发写时复制机制
xdebug_debug_zval(“a”, “b”);
结果:
a: (refcount=1, is_ref=0)=’ 许铮的技术成长之路 ’
b: (refcount=1, is_ref=0)=’ 许铮的技术成长之路 1 ′
写时改变原理
变量之间的赋值我们搞清楚了,那么变量和引用之间的赋值呢?我们还是通过举例来说明
举例:
$a = “ 许铮的技术成长之路 ”;
$b = &$a;
xdebug_debug_zval(“a”, “b”);
结果:
a: (refcount=2, is_ref=1)=’ 许铮的技术成长之路 ’
b: (refcount=2, is_ref=1)=’ 许铮的技术成长之路 ’
此时,我们发现,变量 a 和 b 的 refcount 还是 2,只不过 is_ref 变成了 1,那是因为在将变量 a 引用赋值给变量 b 时,在原变量容器上作了修改,将 is_ref 变成了 1,且 refcount+1
那如果引用赋值的基础上又发生了变量的改变了呢?
举例:
$a = “ 许铮的技术成长之路 ”;
$b = &$a;
$b = “ 许铮的技术成长之路 1 ”
xdebug_debug_zval(“a”, “b”);
结果:
a: (refcount=2, is_ref=1)=’ 许铮的技术成长之路 1 ′
b: (refcount=2, is_ref=1)=’ 许铮的技术成长之路 1 ′
是不是觉得很神奇?变量 b 和变量 a 的值一起发生改变了~ 其实这是因为触发了写时改变原理
写时改变原理触发时机:is_ref 为 1 的变量容器在被赋值之前,优先检查变量容器的 is_ref 是否等于 1,如果为 1,则不进行写时复制,而是在原变量容器基础上作内容修改;而如果将 is_ref 为 1 的变量容器赋值给其他变量时,则会立即触发写时复制
那么如果把刚刚举得几个例子合并在一起呢?最后结果又是什么呢?
举例:
$a = “ 许铮的技术成长之路 ”;
$b = $a;
$c = &$a;
xdebug_debug_zval(“a”, “b”, “c”);
结果:
a: (refcount=2, is_ref=1)=’ 许铮的技术成长之路 ’
b: (refcount=1, is_ref=0)=’ 许铮的技术成长之路 ’
c: (refcount=2, is_ref=1)=’ 许铮的技术成长之路 ’
整体执行过程是这样的,当执行到第二行时,变量容器的 refcount 会变成 2,变量 a 和变量 b 共享同一个变量容器;当执行到第三行时,因为将变量 a 的引用赋值给变量 c,但是变量 b 和变量 a 已经共享了同一个变量容器,此时变量容器如果要发生改变,因为 refcount>2,所以会发生写时复制,将变量 a 和变量 b 分离,之后将变量 a 引用赋值给变量 c 时,则会原基础上进行修改,is_ref 变成 1,且 refcount 变成 2
思考
那么,下面的这个例子,最终结果是什么呢?欢迎大家在下方留言或私信我~
举例:
$a = “ 许铮的技术成长之路 ”;
$b = $a;
$c = &$a;
$d = $a;
$e = “ 许铮的技术成长之路 1 ”
$a = $e;
xdebug_debug_zval(“a”, “b”, “c”, “d”, “e”);
如果你喜欢我的文章,请点赞支持我下,并欢迎关注我的专栏,每周都会有原创且有深度的文章奉上哟~

正文完
 0