深入理解php的引用赋值

关于php引用的一般问题大家看资料就行了,这次我们来聊点有趣的东西。今天一个朋友在群里面问起来一个关于变量引用赋值的问题,问题本身很简单,我突然想做一个实验,来看看array直接赋值和引用赋值性能上的差别,写完代码发现另外一个问题.请看代码

<?php
$a = array_fill(0, 1000000, 10);

function test_time_cost($loop, $func) {
    $start = microtime(true);
    for($i = 0; $i < $loop; $i++) $func();
    $elapsed = (microtime(true) - $start) / $loop;
    echo "elapsed $elapsed\n";
};

function fetch_data($b) {}

test_time_cost(10000, function() {
    global $a; fetch_data(&$a);
});

test_time_cost(10000, function() {
    global $a; fetch_data($a);
});

代码在第二个测试函数里出错了,内存不够了。但是问题是php在变量赋值以后,如果目标变量未被使用,是不会执行赋值操作的。因为php语言在处理变量赋值时实际上是给$a和$b开辟了一个变量容器,在容器里$a和$b是对等的,可以简单理解为引用关系。但是当$b被改动时,$b就会被移出容器。

<?php

$a = array_fill(0, 1000000, 10);
$b = $a;      // 内存不增加
$b[0] = 10; // $a所占内存会赋值给$b

fetch_data函数实际上未对变量做任何处理,怎么会触发复制呢?于是我写了下面的代码


<?php

echo "memory_limit = " . ini_get('memory_limit') ."\n";

echo "memory init = " . memory_get_usage(true) . "\n";
$a = array_fill(0, 10000, 10);
echo "memory after a = " . memory_get_usage(true) ."\n";
$c = $b = $a;
echo "memory after c,b = " . memory_get_usage(true) ."\n";

function fetch_data($m) {
    echo "memory in fetch_data = " . memory_get_usage(true) ."\n";
}

fetch_data($a);

global $a; fetch_data($a);


运行结果如下


memory_limit = 128M
memory init = 786432
memory after a = 1835008
memory after c,b = 1835008
memory in fetch_data = 1835008
memory in fetch_data = 3670016

说明在声明global $a以后发生了变量拷贝赋值,既然是这样,看看php的文档吧,是不是对global申明的变量有什么特殊处理。看了一遍,没什么特殊的,就是创建引用对象。于是我又仔细看了一次php关于引用的说明,http://php.net/manual/en/features.gc.refcounting-basics.php. 我这次准备跟踪一下引用计数器

$a = array_fill(0, 1, 10);
$c = $b = $a;
echo "c,b,a\n";
xdebug_debug_zval('a');
xdebug_debug_zval('b');
xdebug_debug_zval('c');

echo "changing b\n";
$b[0] = 8;
xdebug_debug_zval('a');
xdebug_debug_zval('b');
xdebug_debug_zval('c');

echo "global a\n";
global $a;
xdebug_debug_zval('a');
xdebug_debug_zval('b');
xdebug_debug_zval('c');

echo "c,b,global a\n";
$c = $b = $a;
xdebug_debug_zval('a');
xdebug_debug_zval('b');
xdebug_debug_zval('c');

运行的结果

c,b,a
a: (refcount=3, is_ref=0)=array (0 => (refcount=1, is_ref=0)=10)
b: (refcount=3, is_ref=0)=array (0 => (refcount=1, is_ref=0)=10)
c: (refcount=3, is_ref=0)=array (0 => (refcount=1, is_ref=0)=10)
changing b
a: (refcount=2, is_ref=0)=array (0 => (refcount=1, is_ref=0)=10)
b: (refcount=1, is_ref=0)=array (0 => (refcount=1, is_ref=0)=8)
c: (refcount=2, is_ref=0)=array (0 => (refcount=1, is_ref=0)=10)
global a
a: (refcount=1, is_ref=1)=array (0 => (refcount=2, is_ref=0)=10)
b: (refcount=1, is_ref=0)=array (0 => (refcount=1, is_ref=0)=8)
c: (refcount=1, is_ref=0)=array (0 => (refcount=2, is_ref=0)=10)
c,b,global a
a: (refcount=1, is_ref=1)=array (0 => (refcount=2, is_ref=0)=10)
b: (refcount=2, is_ref=0)=array (0 => (refcount=2, is_ref=0)=10)
c: (refcount=2, is_ref=0)=array (0 => (refcount=2, is_ref=0)=10)

终于看出了问题,在没有global $a的时候,a,b,c在同一个变量容器里,所以没有发生复制行为。但是在声明global $a以后再进行复制,b,c不和a在同一容器了。我觉得原因就在于$a这个时候已经不是简单的变量了,可以理解为一个指针。php在这个时候没有办法创建变量容器同时给变量和指针,所以拷贝于是就发生了。

希望今天这个实验能加深大家对php引用的理解。我的微博 "James胡建",QQ号码 914244905,欢迎关注我。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值