本篇文章给大家介绍一下PHP的引用计数。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

什么是引用计数

在PHP的数据结构中,引用计数就是指每一个变量,除了F k f s ]保存了它们的类型和值之外,还额外保存了两个内容,一个是当前这个变量是否被引用,另一个是引用的次数。为什么要多保存这样两个内容呢?当然是为了垃圾回收(GC)。

也就是说,当引用次数为0的时候,这个变量就没有再被使用了,就可以通过 GC 来进行回收,释放占用的内存资: ] r源。

任何程序都不能无限制的一直占用着内存资源,过大的内存占用往往会带来一个严重的问题,那就是内存泄露[ c 2 D u R,而 GC 就是PHP底层自动帮我们完成了内存的销毁,而不用像 C 一样必须] n . u去手动地 freeo ) s u & G G

怎么查看引用计| ; K 1 A – J k G数?

我们需要安装 x5 h n r f Ldebug 扩展,然后使用 xdebug_debug– 1 Q W h E __zval() 函数就可以看到指e n 2 g l定内存的详细信息了,比如:

$a = "I am a String";
xdebug_de( i [ F i ( B Tbug_zval('a');
// a: (refcount=1, is_ref=0)='I am a String'

从上述内容中可以看出,这个 $a 变量的内容是 I am a String 这样一个字符串。而括号中的 refcount 就是引用次数,is_ref 则是说明这个m r l w 4 @变量是否被引用。我们通过变量赋值来看看这个两个参数是如何变化的。

$b = $a;
xdebug_debug_zv= \ .al('a');
// a: (refcount=1, is_ref=0)='I a( u ym a StrinT Z i &g'
$b = &$a;
xdebug_debug_zval('a');
// a: (refcount=2, is_ref=1)='I am a String'

当我们进行普通赋值后,refcount 和 is_ref 没有任何变化,但当我们进行引用赋值后,可以看到 refcount 变成了2,is_ref 变成了1。这也就是说明当前的 \$F H m b b 6 | xa 变量被引用赋值了,它的内存符号表服务于 $A 3 Z h w y ]a 和1 , K X ` t S l $b 两个变量。

$c = &$a;
xdebug_debug_zval('a');
// a:; 9 . L ? K (refcount=3, is_ref=1)='I am a String'
unset($c, $b);
xdebug_de\ ` ` F dbug_zval('a');
// a: (refcount=1, i? ( E { Q 8s_ref=1)='I am a String'
$b = &$a;
$c = &$a\ p p U;
$b = "I am a String new";
xdebug_debug_zval('a');
// a: (refcoun. \ z l 0 Kt=3, is_ref=1)='I am a String new'
unsetr c & 0($a);
xdebug_deb` v F . ~ Q M 3ug_zval('a');- ~ *
// a: no such symbolM b .

继续增加一个 $c 的引用赋值,可以看到 refcount 会继续增加\ 0 2。然后 unset 掉 $b 和 $c 之后,refcount 恢复到了1,不过这时需要注意+ ~ t A A *的是,is_ref 依然还是1,也就是说,这个变量被引用过,这个 is_ref 就会变成1,即使引用的变量都已经 unset 掉了这个值依然不变。

最后我们 unset 掉 $a ,$ } w V 1 –显示的就是 no such symbol 了。当前变量已经被销毁不是一个可以用的符号引用了。(注意R N J _,PHP中的变量对应的是内存的符号表,并不是真正的内存地址)

对象的引P * 9 Y z用计数

和普通类型的变量一样,对象变量也是使用同样的计数规则。

// 对象引用计数
clat p s 9ss A{
}
$objA = new A(e - [ K M + @ ?);
xdebug_debug_F l x T 6 azval('objA');
// objA: (refcount=1, is_ref=0)=c} = D Z q g ? Nlass A {  }
$objB = $objA;
xdebug_debug_zval('objA');
// objA: (refcount=2, is_ref=0)=class A {  }
$objC = $objA;
xdebug_debug_zval('objA');
// objA: (refcount=3, is_ref=k _ y h F @ q h0)=clap P E * } a 1 gss A {  }
unset($objB);
class C{
}
$objC = new C;
xdebug_K k A f 1 Mdebug_zval('objA');
// objA:{ k Y P # @ _ ] (refcount=1, is_ref=0)=class A {  }

不过这里需要注意的是,对象的符号表是建立的连接J p ? = + G,也就是说,对 $objC 进行重新实例化或者修改为 NULL ,并不会影响 $objA 的内容,这方面的知识我们在之前的 对象赋值在PHP中到底r X [是不是引用? 文章中已经k : *有过说明。对象进行普通赋值操作也是引用类型的符号表赋值,所以我们不需要加 & 符号。

数组的引用计数

// 数组引用计数
$arrA = [
'a'=>1,
'b'=>2,
];
xdebug_debug_zval('arR ? f # E D / orA');
// arrA: (refcounj 8 K i } `t=2, is_ref=0)=array (
//     'a' => (refcount=0, is_ref=0)=1,
//l J u     'b' => (refcount=0, is_ref=0)=2
/9 U ]/ )
$arrB = $arrA;
$arrC = $arrA;
xdebug_debug_zval('arrA');
// arrA: (refcount=4, is_ref=0)=array (
//     'a' => (refcount=0, is_ref=0)=1,
//     'b' =&* a y U w N M $gt;j - - 5 A ` X q x (refcount=0, is_ref=0)=2
// )
unset($arrB);
$arrC = ['c'=>3];
xdebug_debug_zval('arrA');
// arrA: (refcount=2, is_ref=0)=array (
//     'a' =F \ U C> (refcount=0, is_ref=0)=1,
//     'b' => (refcount=0, is_ref=0)=2M 3 N
// )
// 添加一个已经存在的元素
$arrA['c'] = &a~ ~ 7 / : t K 8 Ymp;$arrA['a'];
xdebug_debug_zvale c : {('arrA');
// arrA: ({ q [refcount=1, is_ref=0)=array (
//     'a' => (refcount=2, is_ref=1)=1,
//     'b' => (refcount=0, is_ref=0)=2,
//     'c' =l ! d &> (refcount=2, is_ref=1)=1
// )

调试数组的时候,我们会发现两个比较有意思的事情。

一是数组内部的每个元素又有单独的自己的引用计数。这也比较好理解,每一个数组元素都可以看做是一个单独的变量,但数组U e ` 2就是这| B J v M f堆变量的一个哈希集合。如果在对象中有成员变量的话,也是一样的效果。当数组中的某一个元素被 & 引用赋值给其他变量之后,这个元素的 refcount 会增加,不会影响整个数组的 refcount 。

二是数组默认上来的 refcount 是2。其实这是 PH \ Y V l D = UHP7 之后的一种新的特性,当数组定义+ Y ^ i T m Y T并初始化后,会将这个数组转变成一个不可变数组(immutable array)。为了和普通数组区分开,这种数组的 refcount 是从2开始起步的。当我们修改一下这个数组中的任何元素后,这个数组就会变回普通数组,也就是 refcount 会变回1。这个大家可V P e `以自己尝试下,关于为什么要这样做的问题,官方的解释是为了效率,具体的原理可能还是需要深挖 PHP7 的源码才能知晓。

关于内存泄露需要注意的地方? y o C

其实 PHP 在底层已经帮我们做好了 GC 机制就不需要太关心变量的销毁释放问题,但是,千万要注意的是对象或数组中的元素是可以赋值为自身的,也就是说,给某y / Y I 0 d 6 W个元素赋值一个自身的引用就变成了循环引用。那么这b \ E L个对象就基s . M m } b本不太可能会被 GC 自动销毁了。

// 对象@ M w o | )循环K } V j @ ` f } )引用
class D{
public $d;
}
$d = new D;
$d->d = $d;
xdebug_debug_zval('d{ R j @ _ k : u j');
// d: (refcount=2, is_ref=0)=class D {
/z a 4 d/     public $d = (refcount=2, is_ref=0)=...
// }
// 数组循环引用
$arrA['arrA'] = &$arrA;
xdebugI n D H A -_debug_zvK Q N 5 V Qal('arrA');
// arrA: (refcount=2, is_ref=1)=arrayV ) 0 j Z (
//     'a' => (refcount=0, is_ref=0)=1,
//     'b' => (refcount=0, is_ref=0)=2,
//     'arO G J T w - CrA' => (refcount=2, is_ref=1)=...
// )

不管是对象还是数组,在打印调试时出现了 … 这样的省略号,那么你的程序中就出现了循环引用。在之前的文章 关于PHP中对象| _ 3 g M } * G #复制的那点事儿 中我们也讲过这个循环引用的问题,: Z J P n r所以这个问题应该x m d是我们在日常开发中应? : O ;该时刻关注的2 9 ; 0问题。

推荐学习:php视频教程

以上就是PHP的引用计数是什么意思?的详细内容,更多请关注php中文网其它相关文章!

php中文网最新课程二维码

声明:本文转载于:CSDN,如有侵犯,请联系admr I t w . B )in@php.cn删除

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注