深入理解变量改变时PHP内核发生的一些变化

《Extending and Embedding PHP》PHP扩展开发和内核应用最好的书,没有之一。对php中变量的引用计数、写时复制,写时改变,写时复制和改变做个”翻译“。

ZVAL

看下面的内容之前先对zval这个结构体做个了解

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。看看下面这张图片

          • 上面这又是另外一种情况,在is_ref = 1的情况下,试图单纯的进行refcount+1操作的时候会分离出来一个新的zval给等号左边的变量,并初始化他,看看下面这张图片


          • 参考文献:
          • 1.《Extending and Embedding PHP》- Chaper 3 - Memory Management.
时间: 2024-11-04 19:08:18

深入理解变量改变时PHP内核发生的一些变化的相关文章

php中引用&的真正理解-变量引用、函数引用、对象引用

php的引用(就是在变量或者函数.对象等前面加上&符号) //最重要就是 删除引用的变量 ,只是引用的变量访问不了,但是内容并没有销毁 在PHP 中引用的意思是:不同的名字访问同一个变量内容. 变量的引用        PHP 的引用允许你用两个变量来指向同一个内容 <?php $a="ABC"; $b =&$a; echo $a;//这里输出:ABC echo $b;//这里输出:ABC $b="EFG"; echo $a;//这里$a的值变

如果常量类进行改变时,只编译常量类,而使用常量的类不重新编码,这样改动实际上算没有生效(转)

在Java开发过程中有很多通用的准则,遵守这些准则能够避免很多不必要的错误发生,让代码的质量更高,下面的内容为书籍第一章<Java开发中通用的方法和准则>的阅读笔记. 一.不要在常量和变量中出现易混淆的字母 例如数字1和小写字母l容易混淆,数字0和字母o容易混淆,因此在变量或常量命名时需要避免两个同时出现. 另外命名最好遵守Java编码规范:包名全小写,类名首字母全大写,常量全部大写并用下划线分隔,变量采用驼峰命名法等等. 二.不要让常量蜕变成变量 常量应该保证在编译期就确定其值不变,而不是在

1.2定义和理解变量

定义和理解变量 所有的现代编程语言,包括Objective-C中,有变量的概念.变量是简单的别名,在存储器中的位置.每个变量具有以下属性:1,一种数据类型,它可以是一个原始的,如整数,或者是一个对象2,名称3:值.你并不总是需要设置的变量值,但是你需要指定它的类型和它的名字.下面是你需要知道的关于编写任何典型的iOS应用程序时的几个数据类型: Mutable Versus Immutable可变vs不可变 如果数据类型是可变的,你可以,如果它被初始化后更改.例如,你可以在一个可变数组改变其中一个

【JavaScript基础】在写冒泡排序时遇到的JavaScript基础问题:JavaScript的数据类型和变量赋值时的原理

写冒泡排序时,遇到一个问题: function bubbleSort(arr){ var temp = 0; console.log("传入的数组:"); console.log(arr); for(var i = 0;i<arr.length;i++){ //循环arr.length-1次 console.log("外层第"+i+"次循环===============start"); for(var j = 0;j<arr.leng

深入理解Java运行时数据区

前情回顾 在本专栏的前12篇博客中, 我们主要大致介绍了什么是JVM, 并且详细介绍了class文件的格式. 对于深入理解Java, 或者深入理解运行于JVM上的其他语言, 深入理解class文件格式都是必须的. 如果读者对class文件的格式不是很熟悉, 在阅读本博客下面的文章之前, 建议先读一下前面的12篇博客, 或者参考其他资料, 熟悉class文件的格式. 在深入理解Java虚拟机到底是什么 这篇博客中, 我们有提到过, JVM就是一个特殊的进程, 我们执行的java程序, 都运行在一个

Linux下通过设置PS1变量改变bash提示符颜色

我们都知道bash中,可以通过PS1变量改变提示符的颜色.当命令输出较长时,往往不容易第一眼看到输出是从哪里开始的,通过改变改变PS1变量,可以更改bash提示符的颜色,这样在人群中多看了一眼,就能够找到输出开始的地方了.另外,设置颜色也可以让命令行更漂亮 我们可以通过设置PS1变量来改变bash的提示符内容,如下: 改变了PS1变量的内容后,bash提示符跟着变化了.PS1变量也可以使用反斜杠"\"来显示类似变量的内容,知道反斜杠转义的应该都不陌生,如下: 在PS1中加上"

jeecg bootstrap框架 构造下拉列表,当选中的值改变时,自动填充其关联控件的值

构造下拉列表:方法之一:使用控件  <t:dictSelect> 对数据库中对应的表进行绑定 1 <div class="bt-item col-md-6 col-sm-6"> 2 <div class="row"> 3 <div class="col-md-3 col-sm-3 col-xs-3 bt-label"> 4 id: 5 </div> 6 <div class=&qu

254 在js调用函数时,传递变量参数时, 是值传递还是引用传递

问题: 在js调用函数时,传递变量参数时, 是值传递还是引用传递 理解1: 都是值(基本/地址值)传递 理解2: 可能是值传递, 也可能是引用传递(地址值) <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>02_关于数据传递问题</title> </head> <body> <

input中的内容改变时触发的事件

onchange事件在内容改变(两次内容有可能相等)且失去焦点时触发: onpropertychange事件是实时触发,每增加或删除一个字符就会触发,通过js改变也会触发该事件,但是该事件是IE专有. oninput事件是IE之外的大多数浏览器支持的事件,在value改变时实时触发,但是通过js改变value时不会触发:基本写原生常用的是oninput. oninput与onpropertychange失效的情况: oninput事件: 1.当脚本中改变value时,不会触发: 2.从浏览器的自