PHP5 内存引用计数与写时复制

内核中变量的存储方式

首先看两个C的结构:


typedef struct _zval_struct {
    zvalue_value value;
    zend_uint refcount__gc;
    zend_uchar type;
    zend_uchar is_ref__gc;
} zval;
typedef union _zvalue_value {
    long lval;                 // 用于 bool 类型、整型和资源类型
    double dval;               // 用于浮点类型
    struct {                   // 用于字符串
        char *val;
        int len;
    } str;
    HashTable *ht;             // 用于数组
    zend_object_value obj;     // 用于对象
    zend_ast *ast;             // 用于常量表达式(PHP5.6 才有)
} zvalue_value;

php弱类型的实现在于以上的C片段。简单解释一下原理:

php每申明一个变量,zval会给变量一个容器,例如 $a=1; 那么系统会给int类型的a 一个容器。容器内部包含以下内容:

a的值--对应的结构体中的zvalue_value value

a的类型--对应结构体的 zend_uchar type

zend 引擎定义如下几种类型

常量定义标识类型
#define IS_NULL是否为空(null)
#define IS_LONG    是否为整型(int)
#define IS_DOUBLE是否为浮点数(float)
#define IS_STRING是否为字符串(string)
#define IS_ARRAY是否为数组(array)
#define IS_OBJECT是否为对象(object)
#define IS_BOOL是否为布尔类型(bool)
#define IS_RESOURCE

是否为资源类型(resource)

不同类型对应结构体的存储:

php语言层类型保存在zvalue_value中的成员变量
long、bool、resourcelval
doubledval
stringstr(len保存长度、val保存值)
arrayht
objectobj

a这个容器是否为引用地址 --对应的是zend_uchar is_ref__gc

a被指向的集合数 --对应的是 zend_uint refcount__gc

当你申明一个变量 $a=1  系统做了如下的事情

创建结构体  ->  设置类型type 为 int   ->  设置值为zvalue_value里的long lval(string类型等其他类型依次类推)  ->  设置是否引用地址为 0 ,即不引用内存地址  ->  a的指向集合数设置为1  -> 将zval容器指向 $a

如果此时,我们将 $b=$a  那么 系统会作如下处理

根据$a 找到对应结构体 ->  设置指向集合数自增 1 ->  将zval 容器 指向 $b

如果 我们在上述基础上使用 $c=& $a 设置c的值捆绑a的地址,系统会做如下处理

根据$a 找到对应结构体  ->  设置指向集合数再次自增 1 -> 设置是否引用地址 为 1  -> 将该zval 容器 指向 $c

此时如果我们 跟新 $c的值为 2 试着sh使用 xdebug_debug_zval打印 a的值会出现如下内容 

a: (refcount=2, is_ref=1)=2
原因: 

因为三个变量都指向zval 的容器。并且 $c=& $a  会更改 zval 模式为引用模式。当其中一个变量值修改时 其他值由于指向同一个zval容器。所以都会改变。

 

引用计数与写时复制

如果执行以下代码:

<?php
$a=1;
xdebug_debug_zval('a');

$b=$a;
xdebug_debug_zval('a');

$a=2;
xdebug_debug_zval('b');
xdebug_debug_zval('a');

那么结果如下 :

a: (refcount=1, is_ref=0)=1
a: (refcount=2, is_ref=0)=1
b: (refcount=1, is_ref=0)=1
a: (refcount=1, is_ref=0)=2

过程分析:   

$a 创建了 指向数为1  非地址引用并且值为int类型 1 的zval 容器;新建一个$b  也指向 $a的zval 容器。现在我们将$a的值赋值为 2 ,由于该zval 容器非地址引用,所以系统会重新创建一个指向数为1  非地址引用并且值为int类型 2的zval 容器 指向$a ,由于 $a 不再指向原zval容器 ,所以原容器的 指向数 减 1。

以上内容 就是php内核的另一个重要的特性。引用计数 与 写时复制 原理。这个原理很好的解决了 内存的复用。

 

转载于:https://my.oschina.net/u/4173863/blog/3084081

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值