引用基本概念:用不同的名字访问同一个变量的内容,用符号&表示
php的cow机制:英文名称copy by write,意思是只有进行修改操作才会copy
1、变量直接赋值的情况
<?php
//定义a
$a = range(0,3);
var_dump(memory_get_usage());//查看内存使用情况
xdebug_debug_zval('a');
//引用
$b = $a;
var_dump(memory_get_usage());
xdebug_debug_zval('a');
//修改a
$a = range(0,3);
var_dump(memory_get_usage());
xdebug_debug_zval('a');
运行结果

从zval值分析
第一个显示连接数是1,不是引用类型。这个容易理解,此刻只有变量a指向该空间
第二个显示连接数是2,不是引用类型。这里对变量b直接赋值,在没有对变量进行操作前始终是指向同一个空间,如下图

第三个zval值显示变量a的连接数为1,不是引用类型。此刻的变量a进行了操作,根据php的cow机制a另外开辟了一块空间并指向该空间,故显示连接数为1。如下图

从内存空间占有量分析
很明显,在$a进行操作后内存空间明显增加,即验证(copy on write机制)了 $a对之前的变量空间进行了复制
2、引用变量的情况
我们将前面的赋值操作改为引用,查看下结果
<?php
$a = range(0,100);
var_dump(memory_get_usage());//查看内存使用情况
$b = &$a;//将$a的地址给$b
var_dump(memory_get_usage());
$a = range(0,100);
var_dump(memory_get_usage());
运行结果如下

可以看见,引用变量后的运行结果内存空间的使用没有明显变大,因为这里使用了引用,引用后的变量永远指向同一个空间地址,如下图

我们再来看如下代码
<?php
//定义a
$a = range(0,3);
var_dump(memory_get_usage());//查看内存使用情况
xdebug_debug_zval('a');
//引用
$b = &$a;
var_dump(memory_get_usage());
xdebug_debug_zval('a');
//修改a
$a = range(0,3);
var_dump(memory_get_usage());
xdebug_debug_zval('a');
打印结果

第一个zval结果显示指向该内存空间中只有一个变量,不是引用类型
第二个zval结果显示指向该内存空间的有两个变量,是引用类型,由代码可知我们确实是让变量b引用变量a
第三个zval结果显示和第二个的一样,即对变量a进行操作后没有进行空间复制,变量a和变量b仍然指向同一块内存空间。(引用的时候其实是拥有该空间的地址)
从内存空间使用中也可看出,在对变量a进行操作后内存空间大小仍是不变。
3、对象的引用
对象本身就是引用传递,即当操作时不会进行对象的复制,要复制可以使用对象的克隆方法。
<?php
class Persosn{
public $name = '张三';
}
$a = new Persosn();
xdebug_debug_zval('a');
$b = $a;
xdebug_debug_zval('a');
$b->name = '李四';
xdebug_debug_zval('a');
运行结果

因为对象本身就是引用传递,故内存中并没有再复制一份出来,连接数依然是2
4、真题
<?php
/*
* 写出如下程序的输出结果
* */
$data = ['a','b','c'];
foreach ($data as $key=>$val){
$val = &$data[$key];
var_dump($data);
}
var_dump($data);
/*
* 程序运行时,每一次循环结束后变量$data的值是什么?请解释
* 程序执行完成后,变量$data的值是什么?请解释
* */
运行结果

第一次循环的时候,$ key=0,$val=a,循环体内 $val 指向 $data[0]的内存空间,故此时循环结果为a,b,c
第二次循环的时候, $ key=1, $val=b,由于 $val指向 $data[0]的内存空间,故此时 $data[0]的值变为b,则输出 $data的值为b,b,c,此时循环体内 $val指向的内存空间为 $ data[1]
第三次循环的也一样, $ key = 2, $val=c,由于 $ val 指向 $ data[1]的内存空间,则此时 $data[1]的值变为c,故输出结 $data的值为b,c,c,此刻 $val指向 $data[2]
因此最终结果为b,c,c
扩展
zval()
php变量的值存在于一个叫‘zval’的容器中,zval包含了该变量的类型和值,还包含两个字节的额外信息。
第一个是“is_ref”,是一个bool值,用来标识这个变量是否是属于引用集合。通过这个字节php引擎能够把普通变量和引用变量区分开,由于php允许用户通过使用&来使用自定义引用,zval变量容器中还有一个内部引用计数机制来优化内存使用。
第二个额外字节是“refcount”,用以表示指向这个zval变量容器的变量(也称符号即symbol)个数。所有的符号存在一个符号表中,期中每个符号都有作用域(scope),那些脚本(比如:通过浏览器请求的脚本)和每个函数或者方法也都有作用域。当一个变量被赋常量值时,就会生成一个zval变量容器。
unset()
只会取消引用,不会销毁内存空间
<?php
$a = 1;
$b = &$a;
unset($b);
var_dump($a);
输出结果

变量b对变量a进行引用,此刻指向同一块内存空间,因为unset()只会取消引用不会销毁内存空间,故此刻a的值任然为1,如下图


768

被折叠的 条评论
为什么被折叠?



