摘要:但是对于结构体中的和字段我们一直都没有详细介绍过,而这两个字段其实是和变量之间赋值的原理有着密切的关系的。
上周我们从底层的角度介绍了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");
如果你喜欢我的文章,请点赞支持我下,并欢迎关注我的专栏,每周都会有原创且有深度的文章奉上哟~
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/31087.html
摘要:数组是最常用的数据类型,同时容易上手也得益于其强大的数组,但是数组在中是如何实现的呢首先,我们还是先了解下相关的数据结构,为下面的内容打好基础哈希表哈希表,顾名思义,即将不同的关键字映射到不同单元的一种数据结构。 数组是PHPer最常用的数据类型,同时php容易上手也得益于其强大的数组,但是数组在php中是如何实现的呢? 首先,我们还是先了解下相关的数据结构,为下面的内容打好基础 哈希...
摘要:对于来说,变量有全局变量和局部变量之分那么,他们都是存储到一个哈希表内了么其实不是的,变量存储也有作用域的概念。 上次跟大家讲了垃圾回收机制后,有些小伙伴对底层原理比较感兴趣,私信问我了一些关于变量的相关知识,既然大家对变量比较感兴趣,那么这次我们来系统的讲一下变量的底层原理 变量结构 首先,我们还是先摆上我们的zval结构体,即php所有变量都会以zval结构体的形式实现 struc...
摘要:总结垃圾回收机制以的引用计数机制为基础以前只有该机制同时使用根缓冲区机制,当发现有存在循环引用的时,就会把其投入到根缓冲区,当根缓冲区达到配置文件中的指定数量后,就会进行垃圾回收,以此解决循环引用导致的内存泄漏问题开始引入该机制 php垃圾回收机制,对于PHPer来说是一个不陌生但是又不是很熟悉的内容。那么php是怎么实现对不需要的内存进行回收的呢? php变量的内部存储结构 首先还是...
摘要:弱类型语言一个变量的类型并不是一开始就确定不变的,运行中才会确定并可能发生隐式或显示的类型转换。引擎组件的模式降低内部耦合。 一、PHP设计理念及特点 多进程模型:由于PHP是多进程模型,不同请求间互不干涉,这样保证了一个请求挂掉不会对全盘服务造成影响,PHP也早支持多线程模型。弱类型语言:一个变量的类型并不是一开始就确定不变的,运行中才会确定并可能发生隐式或显示的类型转换。引擎(Ze...
摘要:虽然有了十全的计划,但如何高效率去记住上面那么多东西是一个大问题,看看我是怎么做的。 前言 前一篇文章讲述了我在三月份毫无准备就去面试的后果,一开始心态真的爆炸,但是又不服气,一想到每次回来后家人朋友问我面试结果的期待脸,越觉得必须付出的行动来证明自己了。 面经传送门:一个1年工作经验的PHP程序员是如何被面试官虐的? 下面是我花费两个星期做的准备,主要分三部分: 有计划——计划好...
阅读 2894·2019-08-30 15:55
阅读 1998·2019-08-30 14:02
阅读 1237·2019-08-29 15:23
阅读 1005·2019-08-29 11:27
阅读 459·2019-08-26 11:43
阅读 3186·2019-08-26 10:32
阅读 1251·2019-08-23 14:41
阅读 3299·2019-08-23 14:41