php 内存分配和管理
程序员文章站
2022-03-24 13:36:42
...
zend解析 php 变量的赋值操作
前提。我们需要知道php 的内核zend:
Zend整体用纯c实现,是php的内核部分,它将php代码翻译(词法、语法解析等一系列编译过程)为可执行opcode的处理并实现相应的处理方法、实现了基本的数据结构(如hashtable、oo)、内存分配及管理、提供了相应的api方法供外部调用,是一切的核心,所有的外围功能均围绕zend实现。
那么我们说到的内存分配和管理,自然是由zend 来完成的,那么zend 是如何针对变量进行内存分配和管理的呢?
Zend整体用纯c实现,是php的内核部分,它将php代码翻译(词法、语法解析等一系列编译过程)为可执行opcode的处理并实现相应的处理方法、实现了基本的数据结构(如hashtable、oo)、内存分配及管理、提供了相应的api方法供外部调用,是一切的核心,所有的外围功能均围绕zend实现。
那么我们说到的内存分配和管理,自然是由zend 来完成的,那么zend 是如何针对变量进行内存分配和管理的呢?
zend 中对变量的声明:
struct _zval_struct { zvalue_value value; /* 变量的值 */ zend_uint refcount__gc; /*符号表中 变量名的个数*/ zend_uchar type; /* 变量当前的数据类型 */ zend_uchar is_ref__gc; /**/ }; typedef struct _zval_struct zval;
一: 变量的简单赋值操作。
看一段php 代码
<?php $a = "xxxx"; $b = $a; $b = "yyy";// 或者 unset($a); ?>
在这里,第一步 $a="xxxx";
$a的值 字符串“xxxx” 保存在毫不相关的结构体zval 中的 value 项。 而变量的名称$a 则是通过 zend 的方法 zend_hash_add把它添加到符号表里。即:也就是将zval的指针的变量存放在了符号表中。这样才能用$a 访问到 “xxxx”。 这个时候,只有$a 指向 zval 所以 refcount=1.
下面是 一个zend 的源码 对赋值操作的解释:
{ zval *fooval; MAKE_STD_ZVAL(fooval); ZVAL_STRING(fooval, "xxxx", 1); ZEND_SET_SYMBOL( EG(active_symbol_table) , "a" , fooval); }
首先,我们声明一个zval指针,并申请一块内存。然后通过ZVAL_STRING宏将值设置为‘xxxx’,最后一行的作用就是将这个zval加入到当前的符号表里去,并将其label定义成a,这样用户就可以在代码里通过$a来使用它了。
第二步 $b = $a ;
将$a赋值给$b ,zend 方法 zend_hash_add把变量名称 zval指针的变量$b 添加到符号表里。 通过结构体zval 达到共享。 从而节省了内存。也就是:符号表中b也是存放的zval指针变量。 符号表中 a b 对应同一个zval指针的地址 所以refcount=2
假如说 第三步操作 是 unset($a);
释放$a的值 zval 中的value。 那么是不是$b 的值也就不存在了?
其实不是这样的,在结构体zval 中有一个属性 refcount . 它会处理掉这样的问题。
refcount : 当变量被创建并且赋值的时候,在符号表中变量 a 通过zval指针 和内存中zval结构体建立联系,这个时候refcount=1 ,当$b=$a.的时候,也就是变量名称b 也是通过zval指针 和内存中zval结构体建立联系,这个时候 refcount=2.,所以unset只需把这个zval的refcount减去1就行了!结果就是 只有 符号表中的b 通过zval类型的指针 和 zval(存放字符串的zval结构体)建立联系 。
假如说 第三步操作是 $b="yyy";
结果肯定是$b 和 $a 各有一个值。当执行赋值的时候,也就是要改变变量$b的值的时候,他们就不能共享zval 了。zend 会判断zval 中的refcount 是否大于1,大于1的话。 这个时候zend 会复制一个的zval 来 存储b , 并且重新定义符号表中的b ,新建一个新的zval指针 指向新的zval
以上是我对变量 在php内存分配的理解。哪里不对 希望大家指出
未完待续。
二:zend 引用变量的赋值