php 变量分离和引用

441 查看

PHP 变量的分离和引用

今天看了一下鸟哥博客变量的分离和引用的内容, 就做了一个笔记!

$var = "I have a dream";
$var2 = $var;
$var3 = $var;

如果我们我们创建一个变量就分配一次内存, 那么像上边的代码,那么就会造成内存的极大浪费. php中的变量是一个指向zval的符号, 那么我们就可以在zval中来优化上边的代码!
phpzval中有一个recount字段, 用来记录当前zval被引用的次数

$var = "abc";           //refcount = 1
$varCopy = $var;        //refcount = 2

创建第一个变量$varrefcount的值为1, 当创建第二个变量$varCopy时, refcount的值为2.我们怎么能查看到这个值呢, 可以通过php提供的debug_zval_dump输出变量的内容和refcount

$var = "abc";               //refcount = 1
debug_zval_dump($var);      //string(3) "abc" refcount(2)
$varCopy = $var;            //refcount = 2
debug_zval_dump($var);      //string(3) "abc" refcount(3)

为什么打印的结果和我们预想的结果不一致呢, 函数debug_zval_dump有一个值传递的形参, 就相当于又执行了一次$arg = $var, 执行这段代码的时候$varrefcount又增加了1,所以在debug_zval_dump内部打印的结果就比实际的要大1.如果我们对一个变量进行unset那么会是一种什么样的效果呢

$var = "abc";
$varCopy = $var;
unset($var);
debug_zval_dump($varCopy);      //string(3) "abc" refcount(2)

如果像下边这段代码会发生什么呢:

    $var = "abc";
    $varCopy = $var;
    $var = 1;

如果一个变量被重新赋值, 在赋值的过程中会首先检测refcount的值, 如果refcount大于1, php就会执行一个分离过程.上边的代码在执行到第三行的时候, php发现$var指向的zvalrefcount大于1, 那么php就会复制一个新的zval出来, 将原来的zvalrefcount减1,并修改变量符号表, 是$var指向新的zval结构 1.这样原来的$var$varCopy就各自指向了不同的结构. 这个就是

copy one write 也叫写时复制

如果我们再加上引用的情况, 那么整个过程就会更加复杂!

$var = "abc";
$varRef = &$var;
$varRef = 1;

最终$var的结果是1, 这个过程被称为

change on write 写时改变

这次的复制是不需要进行变量分离的,需要用到zvalis_ref字段, 对于上边的代码, 当第二行执行以后, $var所代表的zvalrefcount变为2, 同时设置is_ref为1.
第三行的时候, php会首先检查$varRefzvalis_ref字段,如果为1, 则不分离.

$var = "abc";
$varCopy = $var;
$varRef = &$var;

上边的情况, 存在copy on write 同时也有change on write, 是怎么运作的呢.
当执行第二行的时候和前边的copy on write一样, $var$varCopy指向相同的zval,refcount为2
当执行第三行的时候, php发现要操作的zval($var所指向的zval)的refcount大于1, php就会执行分离操作, 把$varCopy分离出去, 并将$var$varRef做change on write关联, 也就是refcount=2, is_ref=1
如果整个过程反过来2

$var = "abc";       //refcount=1, is_ref=0
$varRef = &$var;    //refcount=2, is_ref=1
$varCopy = $var;    //refcont=1, is_ref=0

第三行代码, 因为上边的变量$var对应的zval有is_ref的存在, 那么当前的变量就会直接复制一份出来, 而不会触发copy on write机制.

我们在看一段代码

$var = "abc";
$varRef = &$var;
debug_zval_dump($var);  //string(3) "abc" refcount(1)

这个结果和预想的又有所差别, 是什么原因的? debug_zval_dump() 需要一个值传递的形参, 那么执行debug_zval_dump($var)的时候就相当于debug_zval_dump($arg = $var) 这样就会造成变量的分离,在debug_zval_dump($arg)就相当于对一个全新的变量执行!


  1. 这个地方按我的理解应该是要全新生成一个zval, 然后修改原来的zval的refcount
  2. 这个是我自己猜想的, 不知道是否正确.请输入代码