php中的变量的使用可以说的上是所有编程中最简单的了,但是您真的理解变量的底层机制吗?
不用担心看完这篇文章您就懂啦~
关于COW 机制(copy on write,写时复制,一种内存优化机制)
$a=range(0,10000);
var_dump(memory_get_usage());
$b=$a;
var_dump(memory_get_usage());//因为COW 机制,此时并不会为变量b单独开辟内存空间
$a=range(0,10000);
var_dump(memory_get_usage());//由于$a被重新赋值,所以会重新开辟内存空间
运行结果:
int(769600)
int(769632) //和上面结果不同是因为多了变量名b
int(1166992)//由于对变量a进行写操作(值不变),所以会copy一份a变量空间
引用变量
php中的引用变量比较简单,“引用” 我觉得可以理解成别名吧,比如某人叫“张三”,绰号“阿三”,其实都指的是一个人。
也就是说,php的变量引用操作的任然是原变量;
$a=range(0,5);
print_r($a);
$b=&$a;
$b=range('a','f');
print_r($a);
print_r($b);
运行结果:
Array
(
[0] => 0
[1] => 1
[2] => 2
[3] => 3
[4] => 4
[5] => 5
)
Array
(
[0] => a
[1] => b
[2] => c
[3] => d
[4] => e
[5] => f
)
Array
(
[0] => a
[1] => b
[2] => c
[3] => d
[4] => e
[5] => f
)
在php中,每个php变量存在一个叫"zval"的变量容器中,可以通过xdebug可以分析变量分布情况。
在代码中使用xdebug_debug_zval查看变量时发现refcount会按照赋值类型进行计数:
(花了点时间分析,认可的话点个赞呗~)
定义一个变量后,默认情况:refcount=1,is_ref=0;
1.如果该变量未被引用过,则is_ref=0, refcount=1+变量赋值的次数
2.若该变量被引用过,则is_ref=1 , refcount=1+引用的次数
$a=range(0,5);
xdebug_debug_zval('a');//只有a变量在使用这个变量容器,so, refcount=1, is_ref=0
$b=$a;//把一个变量赋值给另一变量将增加引用次数(refcount).
$c=&$a;
$d=&$a;
$e=$a;
$f=$a;
$g=$a;
xdebug_debug_zval('a');//因为a被引用赋值过,所以is_ref=1,refcount只会统计引用赋值的个数(refcount=refcount+2)
//下面可以把上面的引用$c=&$a;$d=&$a;删掉然后依然查看a变量xdebug_debug_zval('a');结果一样,此处是为了运行效果
xdebug_debug_zval('e');//因为e未被引用赋值过,所以is_ref=0,refcount会统计直接赋值的个数(refcount=refcount+4)
运行结果:
a:
(refcount=1, is_ref=0)// 默认情况下
array (size=6)
0 => (refcount=0, is_ref=0)int 0
1 => (refcount=0, is_ref=0)int 1
2 => (refcount=0, is_ref=0)int 2
3 => (refcount=0, is_ref=0)int 3
4 => (refcount=0, is_ref=0)int 4
5 => (refcount=0, is_ref=0)int 5
a:
(refcount=3, is_ref=1)// 该变量容器被变量a , c , d指向
array (size=6)
0 => (refcount=0, is_ref=0)int 0
1 => (refcount=0, is_ref=0)int 1
2 => (refcount=0, is_ref=0)int 2
3 => (refcount=0, is_ref=0)int 3
4 => (refcount=0, is_ref=0)int 4
5 => (refcount=0, is_ref=0)int 5
e:
(refcount=5, is_ref=0)// 该变量容器被变量a , b , e , f , g指向
array (size=6)
0 => (refcount=0, is_ref=0)int 0
1 => (refcount=0, is_ref=0)int 1
2 => (refcount=0, is_ref=0)int 2
3 => (refcount=0, is_ref=0)int 3
4 => (refcount=0, is_ref=0)int 4
5 => (refcount=0, is_ref=0)int 5
unset
unset只会取消引用,不会销毁空间
下面的代码让你秒懂:
$a="hello";
$b=&$a;
unset($b);
echo $a;
运行结果:hello
//后面两行改成:
unset($a);
echo $b;
运行结果依然不变。
上面的运行结果显而易见了吧 _
对象
php中的对象本质就是引用,不用加符号&。
class Person{
public $name="xiao ming";
}
$p1=new Person();
xdebug_debug_zval('p1');
$p2=$p1;//此处本质是引用,不是对象克隆,我测试,写成p2=&$p1也可以,本质都是引用赋值
$p2->name="xiao qiang";
xdebug_debug_zval('p1');
运行结果:
p1:
(refcount=1, is_ref=0)
object(Person)[1]
public 'name' => (refcount=2, is_ref=0)string 'xiao ming' (length=9)
p1:
(refcount=2, is_ref=0)
object(Person)[1]
public 'name' => (refcount=0, is_ref=0)string 'xiao qiang' (length=10)
既然p2是p1的引用,为啥 is_ref不等于1呢?
若写成写成p2=&$p1 ,is_ref是等于1的,这可能就是对象变量特殊性吧!