php变量的引用计数器和写时复制

众所周知,PHP是不支持指针的,但是如果希望两个变量同时指向同一内存块怎么办呢?为了解决这个问题,PHP内核里使用了引用计数器。

上篇博文介绍了PHP变量在内核中的存储方式了,zval结构中下面两个成员变量用于引用计数器:

is_ref  BOOL值,标识变量是否是引用集合。
refcount  计算指向引用集合的变量个数。

看下面的php代码

<?php
    $a = "this is a";
?>

一个zval结构的实体称为zval容器。在php语言层创建一个变量就会相应地在php内核中创建一个zval容器。因为上面的代码创建了一个变量$a,所以在php内核中会创建一个zval容器;又因为这个变量不是一个引用,所以zval容器的is_ref等于FALSE,并且refcount等于1.

再看下面的代码

<?php
    $a = "this is a";
    $b=$a;
?>

上面这段代码创建了两个变量a和b,所以php内核会创建两个zval容器来保存他们。变量b被赋予变量a的值,那么现在变量a对应的isref字段为何值呢?由于变量并不是引用变量a,所以变量a的is_ref字段的值为FALSE,这个容易理解。但是如果使用xdebug打印变量a的话,会发现refcount等于2,为什么是2呢。这里首先得知道php的写时复制机制。写时复制是一个解决内存复用的办法。例如上面的代码,如果简单地把a的值赋给$b,就有两个”this is a”字符串的复制,这样不利于内存复用。因为完全可以使用一个”this is a”字符串的复制完成工作。所以简单的赋值复制是非常耗费内存的,写时复制就是为了解决这个问题。

写时复制,就是当变量的值改变时才进行内存的复制。要理解写时复制,先看下面的代码:

<?php
    $a="this is a";
    xdebug_debug_zval(‘a‘);

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

    $a="changed value";
    xdebug_debug_zval(‘a‘);
?>

上面这段代码使用xdebug调试工具。输出的结果如下:

a:
{refcount=1,is_ref=0} string ‘this is a‘ {length=10}
a:
{refcount=2,is_ref=0} string ‘this is a‘ {length=10}
a:
{refcount=1,is_ref=0} string ‘changed value‘ {length=13}

上面所示,当将变量a的值赋给变量b时,变量a的refcount增加1,所以这时候变量a跟变量b是指向同一内存块的。当变量a的值改变时,发现refcount 的值变回1,所以这时变量a和变量b指向不同的内存块,这就是读写复制机制。就是两个指向同一内存块的变量,当其中一个变量的值发生变化,才会另外创建一个内存块去保存新的值。

看最后一种情况,如果用户在php脚本中显式地让一个变量引用另一个变量,php内核会如何处理呢?看下面的代码:

<?php
    $a=1;
    xdebug_debug_zval(‘a‘);
    $b=&$a;
    xdebug_debug_zval(‘a‘);
    $b+=5;
    xdebug_debug_zval(‘a‘);
?>

上面的代码输出结果为:

a:
{refcount=1,is_ref=0} int 1
a:
{refcount=2,is_ref=1} int 1
a:
{refcount=2,is_ref=1} int 6

可以看到,当显式地让一个变量引用另一个变量时,变量的is_ref字段会设置为1,表示此变量被引用,另外引用计数器(refcount)也相应的加1,。而在php内核中通过下面代码判断是否复制变量:

if((*valval)->is_ref ||(*valval)->refcount<2 ) {
    return $valval;
}

从上面的代码中可以知道,当变量被引用或者计数器小于2时会直接返回变量的指针(直接返回变量的实体,而不复制变量的值)。当修改一个被引用变量的值时,所有引用它的变量其值也会被修改,因为它们指向同一个zval容器。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-13 18:02:35

php变量的引用计数器和写时复制的相关文章

php 垃圾回收机制----写时复制和引用计数

PHP使用引用计数和写时复制来管理内存.写时复制保证了变量间复制值不浪费内存,引用计数保证了当变量不再需要时,将内存释放给操作系统. 要理解PHP内存管理,首先要理解一个概念----符号表. 符号表的概念: 一个变量有两部分组成:变量名和变量值.而符号表就是将变量名映射到内存中变量值所在地址的数组. 写时复制: 当一个变量的值复制到另一个变量时,PHP没有为复制值使用更多的内存.相反,他会跟新符号表来说明这两个变量拥有相同的内存块.所以下面的代码实际上并没有创建新数组: <?php $peopl

再谈QVector与QByteArray——Qt的写时复制(copy on write)技术

Qt作为一个优秀的跨平台开源C++框架,如果我们只停留在使用它的基础上而不深挖其实现手法,实在是浪费这个知识宝库了~我们在之前的博文QVector的内存分配策略与再谈QVector与std::vector--使用装饰者让std::vector支持连续赋值中简单聊了聊QVector内存分配和赋值方面的一点东西,今天接着从QVector展开谈谈Qt的写时复制技术.老实说,"隐式共享,引用计数,写时复制"也是老调重弹的话题了,不过也是QTL与STL最大的区别之一,这篇博文不详谈"写

PHP &quot;数组变量&quot;之&quot;写时复制的要点&quot; 只有数组才有的概念。

1.如果数组指针位置非法,复制时,会将新数组指针初始化! 2.值传递时,PHP采用了一个COW(写时复制,copy on write)的优化措施! 写时复制的两个要点: <?php $arr1 = array('吕布','赵云','典韦'); end($arr1);next($arr1); //非法了 $arr2 = $arr1; //复制数组////var_dump($arr2);echo '<br>';var_dump(current($arr2));//初始化$arr2      

php变量之写时复制机制(copy on write)

编程思想虽然可以共用,不过语言间的差异还是比较明显的,只是使用者之间没有意识到而己,而了解其中的差异对于编写程序以及把握性能还是有好处的.下面我们来介绍下PHP的一个很重要的机制copy on write,我们先以最简单的变量来介绍这个机制,在说这个之前,笔者先来介绍下弱类型是怎么实现的. 大家都知道,PHP是由C实现的,可是C是强类型语言,PHP怎么做到弱类型语言.一起来看下,PHP变量在C语言低层中的代码, typedef struct _zval_struct zval; typedef

php引用和写时复制

在php变量中已经发现 zval结构体中有refcount__gc(引用个数) 和 is_ref__gc(是否被引用) 例如: <?php $a="hello world"; ?> 此时PHP会创建一个zval容器 因为这个变量不是一个引用 所以这个容器的is_ref__gc为false 并且refcount__gc为1 再看下面的代码 <?php $a="hello world"; $b=$a; ?> 这里由于$b并不是引用$a 所以这里的

PHP 内存管理 写时复制 垃圾回收

PHP中的变量是不需要手动释放的,内核帮我们实现了变量的内存管理,包括内存的分配和回收 变量深拷贝带来的问题就是效率和内存浪费严重. 解决深拷贝:1.引用计数 2.写时复制 PHP变量的内存管理就是基于这两点实现的 当变量赋值.引用的时候不是进行深拷贝,而是多个变量共用一个value,引用计数来记录这个变量被引用过多少次,当其中的一个变量发生变化时将无法与其他的变量共用 value时,这个时候就需要进行深拷贝进行分离value,这就是写时复制. 引用计数: 用来记录当前有多少个zval指向同一个

php底层--4 写时复制

变量的赋值与引用 例如:$a=3; $b=$a; 这个时候是否就产生了2个结构体呢? No,如果是的话,这两个结构体的type,value全都一样,很浪费呀,所以在PHP实现的时候并没有copy一个结构体出来,而是$a,$b共用一个结构体. 在传值赋值时,并没有新生结构体,而是共用的. $a=3; 产生一个结构体 zvalue:3; type:IS_LONG; refcount_gc:1; is_ref_gc:0; $b=$a; 这个时候并没有新产生一个结构体,而是原来的结构体 refcount

PHP写时复制

原文:http://www.huamanshu.com/blog/2014-05-18.html 起源 写时复制英文名字叫“copy-on-write”,简称“cow”,又名“靠” 今天查了下这个"cow",原来起源于*nix内存管理机制,后来广泛应用在其它开发语言上,C++的STL,还有PHP,相信很多人都了解这个写时复制,经常跟别人侃得甚欢. $foo = 'foo'; $bar = $foo; 不会 初写这样的PHP代码时,常会问,这样的话会不会因为复制而占用更多内存,旁边会有人

47-引用计数与写时复制

47-引用计数与写时复制 对于PHP这种需要同时处理多个请求的程序来说,申请和释放内存的时候应该慎之又慎,一不小心便会酿成大错.另一方面,除了要安全申请和释放内存外,还应该做到内存的最小化使用,因为它可能要处理每秒钟数以千计的请求,为了提高系统整体的性能,每一次操作都应该只使用最少的内存,对于不必要的相同数据的复制则应该能免则免.我们来看下面这段PHP代码: <?php $a = 'Hello NowaMagic!'; $b = $a; unset($a); ?> 第一条语句执行后,PHP创建