深入理解变量改变时PHP内核发生的一些变化
2018-03-08 17:03
543 查看
《Extending and Embedding PHP》PHP扩展开发和内核应用最好的书,没有之一。对php中变量的引用计数、写时复制,写时改变,写时复制和改变做个”翻译“。ZVAL看下面的内容之前先对zval这个结构体做个了解
zval结构体中共有4个元素,value是一个联合体,用来真正的存储zval的值,refcount用来计数该zval被多少个变量使用,type表示zval所存储的数据类型,is_ref用来标志该zval是否被引用。引用计数
我们一起来剖析下上面这段代码:
将变量
在内核中大概是这样的,其中
上面这段代码执行完之后,一般肯定希望
这个具体是怎么实现的呢,我们一起来看下:
复制一个和
将
初始化生成的新zval,设置refcount=1,is_ref=0。
让
对新生成的zval进行操作,这就是写时复制。
下面看看内核中分离时的主要代码:
写时改变
上面这段代码执行完之后一般希望是:
如果
这时候再去修改
分离的问题
说道现在聪明的你可能已经看出点问题了,如果一个zval结构体既有refcount计数又有is_ref引用这个时候怎么办?
如果出现上面这种情况的时候,如果
如果对一个
对这个zval进行初始化,对之前的zval的refcount进行减1操作,让等号左边的变量指向这个新的zval,refcount进行加1操作,is_ref=1。看看下面这张图片
<
9cbc
/p>
上面这又是另外一种情况,在
参考文献:
1.《Extending and Embedding PHP》- Chaper 3 - Memory Management.
zval结构体中共有4个元素,value是一个联合体,用来真正的存储zval的值,refcount用来计数该zval被多少个变量使用,type表示zval所存储的数据类型,is_ref用来标志该zval是否被引用。引用计数
我们一起来剖析下上面这段代码:
$a = 'Hello World';首先这句代码被执行,内核创建一个变量,并分配12字节的内存去存储字符串'Hello World'和末尾的NULL。
$b = $a;接着执行这句代码,执行这句的时候内核里面发生了什么呢?对
$a所指向的zval中的refcount进行加1操作。
将变量
$b指向
$a所指向的zval。
在内核中大概是这样的,其中
active_symbol_table是当前的变量符号表
unset($a);这句代码执行后,内核会将a对应的zval结构体中的refcount计数减一,b还和原来一样写时复制
上面这段代码执行完之后,一般肯定希望
$a=1,$b=6,但是如果像引用计数那样,
$a和
$b指向相同的zval,修改
$b之后
$a不是也变了?
这个具体是怎么实现的呢,我们一起来看下:
$a = 1;内核创建一个zval,并分配4个字节存储数字1。
$b = $a;这一步和引用计数中的第二步一样,将
$b指向和
$a相同的zval,并将zval中的引用计数值refcount加1。
$b += 5;关键是这一步,这一步骤发生了什么呢,怎么确保修改之后不影响
$a。其实Zend内核在改变zval之前都会去进行
get_var_and_separete操作,如果recfount>1,就需要分离就创建新的zval返回,否则直接返回变量所指向的zval,下面看看如何分离产生新的zval。
复制一个和
$b所指向zval一样的zval。
将
$b所指向的zval中的refcount计数减1。
初始化生成的新zval,设置refcount=1,is_ref=0。
让
$b指向新生成的zval。
对新生成的zval进行操作,这就是写时复制。
下面看看内核中分离时的主要代码:
写时改变
上面这段代码执行完之后一般希望是:
$a == $b == 6。这个又是怎么实现的呢?
$a = 1;这一步骤和写时复制中的第一步一样。
$b = &$a;这一步骤内核会将
$b指向
$a所指向的zval,将zval中的refcount加1,并将zval中的is_ref置为1。
$b += 5;这一步骤和写时复制中的第三步骤一样,但是内核中发生的事情却不一样。内核看到
$b进行变化的时候,也会执行get_var_and_separate函数,看是否需要分离。
如果
(*varval)->is_ref的话也会直接返回
$b所指向的zval,不去分离产生新的zval,不管zval的refcount是否>1。
这时候再去修改
$b值,
$a的值也就改变了,因为他们指向相同的zval。
分离的问题
说道现在聪明的你可能已经看出点问题了,如果一个zval结构体既有refcount计数又有is_ref引用这个时候怎么办?
如果出现上面这种情况的时候,如果
$a、$b、$c指向同一个zval结构体,进行改变的时候Zend到底去听谁的?其实这个地方不会指向同一个zval了。
如果对一个
is_ref = 0 && refcount >1的zval进行写时改变这种赋值形式(就是引用赋值)的时候,Zend会将等号右边的变量分离出来一个新的zval,
对这个zval进行初始化,对之前的zval的refcount进行减1操作,让等号左边的变量指向这个新的zval,refcount进行加1操作,is_ref=1。看看下面这张图片
<
9cbc
/p>
上面这又是另外一种情况,在
is_ref = 1的情况下,试图单纯的进行refcount+1操作的时候会分离出来一个新的zval给等号左边的变量,并初始化他,看看下面这张图片
参考文献:
1.《Extending and Embedding PHP》- Chaper 3 - Memory Management.
相关文章推荐
- PHP面向对象之旅:深入理解static变量与方法
- [李景山php] 深入理解PHP内核[读书笔记]--第三章:变量及数据类型--变量的结构和类型--HashTable
- [李景山php] 深入理解PHP内核[读书笔记]--第三章:变量及数据类型--变量的结构和类型--链表
- [李景山php] 深入理解PHP内核[读书笔记]--第三章:变量及数据类型--变量的结构和类型--预定义变量
- [李景山php] 深入理解PHP内核[读书笔记]--第三章:变量及数据类型--global语句
- 深入理解PHP内核的大纲
- php变量 在4.1.0前后的一些变化
- 深入理解PHP原理之变量(Variables inside PHP)
- 深入理解php内核 编写扩展_III- 资源
- [PHP] 深入理解PHP内核:变量及数据类型
- 深入PHP内核(一)——弱类型变量原理探究
- 深入理解php $this 变量是一个到主叫对象的引用
- 深入理解PHP原理之变量(Variables inside PHP)
- [李景山php] 深入理解PHP内核[读书笔记]--第三章:变量及数据类型--数据类型转换
- 深入理解PHP变量的值类型和引用类型
- 深入PHP内核(一)——弱类型变量原理探究
- 深入理解PHP内核(十四)类的成员变量及方法
- 深入理解PHP原理之变量作用域(Scope in PHP)
- 深入理解php内核 编写扩展_III- 资源
- 深入PHP内核(一)——弱类型变量原理探究