在PHP 中引用的意思是:不同的名字访问同一个变量内容.使用&表示
使用 & 会使(如$a = &$b)指向同一个内存地址(这并不像 C 的指针:例如你不能对他们做指针运算,他们并不是实际的内存地址),一个发生改变,另一个也会发生改变
2、使用 memory_get_usage() 函数观察内存的变化
2.1、不使用 &
因为PHP 中COW(Copy On Write) 会导致赋值是引用上一个变量的地址(内存不会发生太大变化),只有在发生 写 操作的时候,才会开辟新的内存地址
$a = range(0,1000); var_dump(memory_get_usage()); $b = $a; var_dump(memory_get_usage()); $a = range(0,1000); var_dump(memory_get_usage());
运行结果:
内存在第一次和第二次并没有太大的差异,第三次产生较大差异
2.2、使用 &
$a = range(0,1000); var_dump(memory_get_usage()); $b = &$a; var_dump(memory_get_usage()); $a = range(0,1000); var_dump(memory_get_usage());
运行结果:
内存在过程中基本没有发生变化,虽然第三步进行了写操作,但是 $a,$b引用的同一个地址,就不需要开辟新地址
3、使用xdebug 观察
xdebug 的安装方法 网上很多,这里不细讲(php 的一个扩展插件而已)
3.1、不使用 &
//zval 变量容器 $a = range(0, 3); xdebug_debug_zval('a'); //定义变量b,把a的值赋值给b $b = $a; xdebug_debug_zval('a'); //修改a $a = range(0, 3); xdebug_debug_zval('a');
运行如下:
refcount用以标识指向这个zval变量容器的变量个数
is_ref(bool),标识此变量是否属于引用集合
第二步只进行了COPY 操作,使$a , $b 指向同一个内存地址, refcount = 2,而第三步 发生了写操作(is_ref=0 不是引用),重新开辟了内存地址,refcount= 1
3.2、使用 &
//zval 变量容器 $a = range(0, 3); xdebug_debug_zval('a'); //定义变量b,把a的值赋值给b $b = &$a; xdebug_debug_zval('a'); //修改a $a = range(0, 3); xdebug_debug_zval('a');
运行结果:
采用了 引用(&),所以 从第二步开始 refcount = 2,is_ref = 1(引用) ,引用状态下不开辟新的内存地址;
4、特殊的引用(对象)
php 中 OBJECT 本身就是引用传值(自 PHP 5 起,new 自动返回引用,因此在此使用 =& 已经过时了并且会产生 E_STRICT 级别的消息。)
//对象本身就是引用传递 class Person { public $name = 'zs'; } $p1 = new Person; xdebug_debug_zval('p1'); $p2 = $p1; xdebug_debug_zval('p1'); $p2->name = 'ls'; xdebug_debug_zval('p1');
运行结果:
OBJECT 赋值情况下 会共享内存地址,但本身又不是引用。
5、unset
//unset 只会取消引用,不会销毁空间 $a = ''; xdebug_debug_zval('a'); $b = &$a; xdebug_debug_zval('a'); unset($b); xdebug_debug_zval('a');
运行结果:
所以在第一步的时候 refcount = 0
对应 引用(&),unset只会取消引用,而不会销毁内存地址
5、总结
通过对 is_ref 判断是否是引用变量,如果是引用变量,修改时直接修改(原内存地址),否则,则需要进行 分离(重新开辟新地址),而 usset 变量只是取消该变量的引用,而不会消除内存地址,只有当refcount = 0;内存才有可能被回收