PHP 变量的分离和引用
今天看了一下鸟哥博客变量的分离和引用的内容, 就做了一个笔记!
$var = "I have a dream";
$var2 = $var;
$var3 = $var;
如果我们我们创建一个变量就分配一次内存, 那么像上边的代码,那么就会造成内存的极大浪费. php中的变量是一个指向zval
的符号, 那么我们就可以在zval中来优化上边的代码!php
的zval
中有一个recount
字段, 用来记录当前zval
被引用的次数
$var = "abc"; //refcount = 1
$varCopy = $var; //refcount = 2
创建第一个变量$var
时refcount
的值为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
, 执行这段代码的时候$var
的refcount
又增加了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
指向的zval
的refcount
大于1, 那么php
就会复制一个新的zval
出来, 将原来的zval
的refcount
减1,并修改变量符号表, 是$var
指向新的zval
结构 1.这样原来的$var
和$varCopy
就各自指向了不同的结构. 这个就是
copy one write 也叫写时复制
如果我们再加上引用的情况, 那么整个过程就会更加复杂!
$var = "abc";
$varRef = &$var;
$varRef = 1;
最终$var
的结果是1, 这个过程被称为
change on write 写时改变
这次的复制是不需要进行变量分离的,需要用到zval
的is_ref
字段, 对于上边的代码, 当第二行执行以后, $var
所代表的zval
的refcount
变为2, 同时设置is_ref
为1.
第三行的时候, php
会首先检查$varRef
的zval
的is_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)
就相当于对一个全新的变量执行!