php字符串拷贝,php7的写时复制机制

1. 什么是写时复制

在《php7引用计数》的文章中,我们知道,对于复制类型的变量,在赋值时,我们并没有重新复制一份数据,而是让新变量的zend_value中相应的指针指向原来的数据,同时增加引用计数。

赋值后,如果其中一个变量试图改变数据内容,就需要重新拷贝一份原数据,同时断开zend_value指向,并改变引用计数。这个过程我们称为写时复制。

下面来看一个例子:

$a = range(0,2);

$b = $a;

xdebug_debug_zval('a');

xdebug_debug_zval('b');

echo "after write\n";

$b[1] = 88;

xdebug_debug_zval('a');

xdebug_debug_zval('b');

输出:

a: (refcount=2, is_ref=0)=array (0 => (refcount=0, is_ref=0)=0, 1 => (refcount=0, is_ref=0)=1, 2 => (refcount=0, is_ref=0)=2)

b: (refcount=2, is_ref=0)=array (0 => (refcount=0, is_ref=0)=0, 1 => (refcount=0, is_ref=0)=1, 2 => (refcount=0, is_ref=0)=2)

after write

a: (refcount=1, is_ref=0)=array (0 => (refcount=0, is_ref=0)=0, 1 => (refcount=0, is_ref=0)=1, 2 => (refcount=0, is_ref=0)=2)

b: (refcount=1, is_ref=0)=array (0 => (refcount=0, is_ref=0)=0, 1 => (refcount=0, is_ref=0)=88, 2 => (refcount=0, is_ref=0)=2)

说明:

赋值后,

math?formula=a%2Cb指向同一个zend_array。引用计数为2。

e39b576ee34e

$b=$a

math?formula=b%E6%97%B6%E8%A1%8C%E4%BF%AE%E6%94%B9%E6%97%B6%EF%BC%8C%E5%8F%91%E7%94%9F%E5%86%99%E6%97%B6%E5%A4%8D%E5%88%B6%EF%BC%8Cb复制一份新的zend_array,再对

math?formula=b%5B1%5D%E8%BF%9B%E8%A1%8C%E4%BF%AE%E6%94%B9%EF%BC%8C%E5%9B%A0%E4%B8%BA%E4%B8%A4%E8%80%85%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0%E9%83%BD%E5%8F%98%E6%88%901%E3%80%82%E6%AD%A4%E6%97%B6%E5%A6%82%E6%9E%9C%E6%9F%A5%E7%9C%8Ba, 它的内容是没有改变的。

e39b576ee34e

修改$b

2. 所有变量都会发生写时复制么?

不是所有类型的变量都可以发生写时复制。

| type | copyable |

+----------------+------------+

|simple types | |

|string | Y |

|interned string | |

|array | Y |

|immutable array | |

|object | |

|resource | |

|reference | |

如上表所示,只有string和array类型的变量,会发生写时复制。

zval.u1.type_flag中记录了当前变量是否可以进行copy。

#define IS_TYPE_COPYABLE (1<<4)

3. 写时复制的启示

理解写时复制对于理解array, string, object类型的赋值修改,及做为函数参数传递后的修改特别重要。

下面我们看一个object的例子:

class Demo{

public $name = 'y';

}

$a = new Demo();

$b = $a;

xdebug_debug_zval('a');

xdebug_debug_zval('b');

$b->name = 'x';

xdebug_debug_zval('a');

xdebug_debug_zval('b');

输出:

a: (refcount=2, is_ref=0)=class Demo { public $name = (refcount=0, is_ref=0)='y' }

b: (refcount=2, is_ref=0)=class Demo { public $name = (refcount=0, is_ref=0)='y' }

a: (refcount=2, is_ref=0)=class Demo { public $name = (refcount=0, is_ref=0)='x' }

b: (refcount=2, is_ref=0)=class Demo { public $name = (refcount=0, is_ref=0)='x' }

说明:

math?formula=b%20%3Da后,两者指向是一个zend_object, 引用计数为2。

因为object类型不会发生写时复制,所以修改

math?formula=b%E7%9A%84name%E5%80%BC%EF%BC%8C%E7%AD%89%E5%90%8C%E4%BA%8E%E4%BF%AE%E6%94%B9a的name值。因此我们看到,修改后,引用计数仍然为2, a,b的name属性值都变为x。

同理,你可以得出下面一段代码的输出为"ball"。

class Demo{

public $name = 'y';

}

$a = new Demo();

$b = $a;

function change($obj){

$obj->name = "ball";

}

change($a);

echo $b->name;

对于array和string类型,因为必会发生写时复制,所以任何情况下对其内部元素的修改都不会影响原值。

结论:

类型

赋值后修改

作为函数参数修改

object

原值改变

原值改变

string

原值不变

原值不变

array

原值不变

原值不变

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值