php变量的引用计数器和写时复制
2015-07-31 16:34
525 查看
众所周知,PHP是不支持指针的,但是如果希望两个变量同时指向同一内存块怎么办呢?为了解决这个问题,PHP内核里使用了引用计数器。
上篇博文介绍了PHP变量在内核中的存储方式了,zval结构中下面两个成员变量用于引用计数器:
看下面的php代码
一个zval结构的实体称为zval容器。在php语言层创建一个变量就会相应地在php内核中创建一个zval容器。因为上面的代码创建了一个变量$a,所以在php内核中会创建一个zval容器;又因为这个变量不是一个引用,所以zval容器的is_ref等于FALSE,并且refcount等于1.
再看下面的代码
上面这段代码创建了两个变量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”字符串的复制完成工作。所以简单的赋值复制是非常耗费内存的,写时复制就是为了解决这个问题。
写时复制,就是当变量的值改变时才进行内存的复制。要理解写时复制,先看下面的代码:
上面这段代码使用xdebug调试工具。输出的结果如下:
上面所示,当将变量a的值赋给变量b时,变量a的refcount增加1,所以这时候变量a跟变量b是指向同一内存块的。当变量a的值改变时,发现
看最后一种情况,如果用户在php脚本中显式地让一个变量引用另一个变量,php内核会如何处理呢?看下面的代码:
上面的代码输出结果为:
可以看到,当显式地让一个变量引用另一个变量时,变量的is_ref字段会设置为1,表示此变量被引用,另外引用计数器(refcount)也相应的加1,。而在php内核中通过下面代码判断是否复制变量:
从上面的代码中可以知道,当变量被引用或者计数器小于2时会直接返回变量的指针(直接返回变量的实体,而不复制变量的值)。当修改一个被引用变量的值时,所有引用它的变量其值也会被修改,因为它们指向同一个zval容器。
上篇博文介绍了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容器。
相关文章推荐
- 一个关于if else容易迷惑的问题
- PHP5.2.*防止Hash冲突拒绝服务攻击的Patch
- 深入理解PHP之匿名函数
- Linux 自检和 SystemTap
- JSP/PHP基于Ajax的分页功能实现
- 关于PHP通过PDO用中文条件查询MySQL的问题。
- 什么是设计模式
- PHP数据库长连接mysql_pconnect的细节
- Php Installing An Expansion
- PHP+Apache在Windows 9x下的安装和配置
- IIS 6 的 PHP 最佳配置方法
- 安装Apache和PHP的一些补充
- Linux Apache+MySQL+PHP
- 建立Apache+PHP+MySQL数据库驱动的动态网站
- PHP 5.3.0 安装分析心得
- apache 环境下 php 的配置注意事项
- ASP.NET、ASP、PHP、JSP之间有什么区别?
- PHP VBS JS 函数 对照表
- C语言实现的统计php代码行数功能源码(支持文件夹、多目录)