站長(zhǎng)資訊網(wǎng)
最全最豐富的資訊網(wǎng)站

什么是引用計(jì)數(shù)?深入淺析PHP中的引用計(jì)數(shù)!

什么是引用計(jì)數(shù)?怎么查看引用計(jì)數(shù)?怎么用引用計(jì)數(shù)?下面本篇文章就來(lái)帶大家了解一下引用計(jì)數(shù),介紹一下引用計(jì)數(shù)的使用方法。

什么是引用計(jì)數(shù)?深入淺析PHP中的引用計(jì)數(shù)!

什么是引用計(jì)數(shù)

在PHP的數(shù)據(jù)結(jié)構(gòu)中,引用計(jì)數(shù)就是指每一個(gè)變量,除了保存了它們的類型和值之外,還額外保存了兩個(gè)內(nèi)容,一個(gè)是當(dāng)前這個(gè)變量是否被引用,另一個(gè)是引用的次數(shù)。為什么要多保存這樣兩個(gè)內(nèi)容呢?當(dāng)然是為了垃圾回收(GC)。

也就是說(shuō),當(dāng)引用次數(shù)為0的時(shí)候,這個(gè)變量就沒有再被使用了,就可以通過(guò) GC 來(lái)進(jìn)行回收,釋放占用的內(nèi)存資源。

任何程序都不能無(wú)限制的一直占用著內(nèi)存資源,過(guò)大的內(nèi)存占用往往會(huì)帶來(lái)一個(gè)嚴(yán)重的問(wèn)題,那就是內(nèi)存泄露,而 GC 就是PHP底層自動(dòng)幫我們完成了內(nèi)存的銷毀,而不用像 C 一樣必須去手動(dòng)地 free 。

怎么查看引用計(jì)數(shù)?

我們需要安裝 xdebug 擴(kuò)展,然后使用 xdebug_debug_zval() 函數(shù)就可以看到指定內(nèi)存的詳細(xì)信息了,比如:

$a = "I am a String"; xdebug_debug_zval('a'); // a: (refcount=1, is_ref=0)='I am a String'

從上述內(nèi)容中可以看出,這個(gè) $a 變量的內(nèi)容是 I am a String 這樣一個(gè)字符串。而括號(hào)中的 refcount 就是引用次數(shù),is_ref 則是說(shuō)明這個(gè)變量是否被引用。我們通過(guò)變量賦值來(lái)看看這個(gè)兩個(gè)參數(shù)是如何變化的。

$b = $a; xdebug_debug_zval('a'); // a: (refcount=1, is_ref=0)='I am a String'  $b = &$a; xdebug_debug_zval('a'); // a: (refcount=2, is_ref=1)='I am a String'

當(dāng)我們進(jìn)行普通賦值后,refcount 和 is_ref 沒有任何變化,但當(dāng)我們進(jìn)行引用賦值后,可以看到 refcount 變成了2,is_ref 變成了1。這也就是說(shuō)明當(dāng)前的

a變量被引用賦值了,它的內(nèi)存符號(hào)表服務(wù)于a 變量被引用賦值了,它的內(nèi)存符號(hào)表服務(wù)于

a 和 $b 兩個(gè)變量。

$c = &$a; xdebug_debug_zval('a'); // a: (refcount=3, is_ref=1)='I am a String'  unset($c, $b); xdebug_debug_zval('a'); // a: (refcount=1, is_ref=1)='I am a String'  $b = &$a; $c = &$a; $b = "I am a String new"; xdebug_debug_zval('a'); // a: (refcount=3, is_ref=1)='I am a String new'  unset($a); xdebug_debug_zval('a'); // a: no such symbol

繼續(xù)增加一個(gè)

c的引用賦值,可以看到refcount會(huì)繼續(xù)增加。然后unsetc 的引用賦值,可以看到 refcount 會(huì)繼續(xù)增加。然后 unset 掉

b 和 $c 之后,refcount 恢復(fù)到了1,不過(guò)這時(shí)需要注意的是,is_ref 依然還是1,也就是說(shuō),這個(gè)變量被引用過(guò),這個(gè) is_ref 就會(huì)變成1,即使引用的變量都已經(jīng) unset 掉了這個(gè)值依然不變。

最后我們 unset 掉 $a ,顯示的就是 no such symbol 了。當(dāng)前變量已經(jīng)被銷毀不是一個(gè)可以用的符號(hào)引用了。(注意,PHP中的變量對(duì)應(yīng)的是內(nèi)存的符號(hào)表,并不是真正的內(nèi)存地址)

對(duì)象的引用計(jì)數(shù)

和普通類型的變量一樣,對(duì)象變量也是使用同樣的計(jì)數(shù)規(guī)則。

// 對(duì)象引用計(jì)數(shù) class A{  } $objA = new A(); xdebug_debug_zval('objA'); // objA: (refcount=1, is_ref=0)=class 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=0)=class A {  }  unset($objB); class C{  } $objC = new C; xdebug_debug_zval('objA'); // objA: (refcount=1, is_ref=0)=class A {  }

不過(guò)這里需要注意的是,對(duì)象的符號(hào)表是建立的連接,也就是說(shuō),對(duì)

objC進(jìn)行重新實(shí)例化或者修改為NULL,并不會(huì)影響objC 進(jìn)行重新實(shí)例化或者修改為 NULL ,并不會(huì)影響

objA 的內(nèi)容。對(duì)象進(jìn)行普通賦值操作也是引用類型的符號(hào)表賦值,所以我們不需要加 & 符號(hào)。

數(shù)組的引用計(jì)數(shù)

// 數(shù)組引用計(jì)數(shù) $arrA = [     'a'=>1,     'b'=>2, ]; xdebug_debug_zval('arrA'); // arrA: (refcount=2, is_ref=0)=array ( //     'a' => (refcount=0, is_ref=0)=1,  //     'b' => (refcount=0, is_ref=0)=2 // )  $arrB = $arrA; $arrC = $arrA; xdebug_debug_zval('arrA'); // arrA: (refcount=4, is_ref=0)=array ( //     'a' => (refcount=0, is_ref=0)=1,  //     'b' => (refcount=0, is_ref=0)=2 // )  unset($arrB); $arrC = ['c'=>3]; xdebug_debug_zval('arrA'); // arrA: (refcount=2, is_ref=0)=array ( //     'a' => (refcount=0, is_ref=0)=1,  //     'b' => (refcount=0, is_ref=0)=2 // )  // 添加一個(gè)已經(jīng)存在的元素 $arrA['c'] = &$arrA['a']; xdebug_debug_zval('arrA'); // arrA: (refcount=1, is_ref=0)=array ( //     'a' => (refcount=2, is_ref=1)=1,  //     'b' => (refcount=0, is_ref=0)=2,  //     'c' => (refcount=2, is_ref=1)=1 // )

調(diào)試數(shù)組的時(shí)候,我們會(huì)發(fā)現(xiàn)兩個(gè)比較有意思的事情。

一是數(shù)組內(nèi)部的每個(gè)元素又有單獨(dú)的自己的引用計(jì)數(shù)。這也比較好理解,每一個(gè)數(shù)組元素都可以看做是一個(gè)單獨(dú)的變量,但數(shù)組就是這堆變量的一個(gè)哈希集合。如果在對(duì)象中有成員變量的話,也是一樣的效果。當(dāng)數(shù)組中的某一個(gè)元素被 & 引用賦值給其他變量之后,這個(gè)元素的 refcount 會(huì)增加,不會(huì)影響整個(gè)數(shù)組的 refcount 。

二是數(shù)組默認(rèn)上來(lái)的 refcount 是2。其實(shí)這是 PHP7 之后的一種新的特性,當(dāng)數(shù)組定義并初始化后,會(huì)將這個(gè)數(shù)組轉(zhuǎn)變成一個(gè)不可變數(shù)組(immutable array)。為了和普通數(shù)組區(qū)分開,這種數(shù)組的 refcount 是從2開始起步的。當(dāng)我們修改一下這個(gè)數(shù)組中的任何元素后,這個(gè)數(shù)組就會(huì)變回普通數(shù)組,也就是 refcount 會(huì)變回1。這個(gè)大家可以自己嘗試下,關(guān)于為什么要這樣做的問(wèn)題,官方的解釋是為了效率,具體的原理可能還是需要深挖 PHP7 的源碼才能知曉。

關(guān)于內(nèi)存泄露需要注意的地方

其實(shí) PHP 在底層已經(jīng)幫我們做好了 GC 機(jī)制就不需要太關(guān)心變量的銷毀釋放問(wèn)題,但是,千萬(wàn)要注意的是對(duì)象或數(shù)組中的元素是可以賦值為自身的,也就是說(shuō),給某個(gè)元素賦值一個(gè)自身的引用就變成了循環(huán)引用。那么這個(gè)對(duì)象就基本不太可能會(huì)被 GC 自動(dòng)銷毀了。

// 對(duì)象循環(huán)引用 class D{     public $d; } $d = new D; $d->d = $d; xdebug_debug_zval('d'); // d: (refcount=2, is_ref=0)=class D {  //     public $d = (refcount=2, is_ref=0)=...  // }  // 數(shù)組循環(huán)引用 $arrA['arrA'] = &$arrA; xdebug_debug_zval('arrA'); // arrA: (refcount=2, is_ref=1)=array ( //     'a' => (refcount=0, is_ref=0)=1,  //     'b' => (refcount=0, is_ref=0)=2,  //     'arrA' => (refcount=2, is_ref=1)=... // )

不管是對(duì)象還是數(shù)組,在打印調(diào)試時(shí)出現(xiàn)了 … 這樣的省略號(hào),那么你的程序中就出現(xiàn)了循環(huán)引用。在之前的文章 關(guān)于PHP中對(duì)象復(fù)制的那點(diǎn)事兒 中我們也講過(guò)這個(gè)循環(huán)引用的問(wèn)題,所以這個(gè)問(wèn)題應(yīng)該是我們?cè)谌粘i_發(fā)中應(yīng)該時(shí)刻關(guān)注的問(wèn)題。

總結(jié)

引用計(jì)數(shù)是了解垃圾回收機(jī)制的前提條件,而且正是因?yàn)楝F(xiàn)代語(yǔ)言中都有一套類似的垃圾回收機(jī)制才讓我們的編程變得更加容易且安全。那么有人說(shuō)了,日常開發(fā)根本用不到這些呀?用不到不代表不應(yīng)該去學(xué)習(xí),就像循環(huán)引用這個(gè)問(wèn)題一樣,當(dāng)代碼中充斥著大量的類似代碼時(shí),系統(tǒng)崩潰只是遲早的事情,所以,這些知識(shí)是我們向更高級(jí)的程序進(jìn)階所不可或缺的內(nèi)容。

測(cè)試代碼:https://github.com/zhangyue0503/dev-blog/blob/master/php/202004/source/PHP%E7%9A%84%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0%E6%98%AF%E4%BB%80%E4%B9%88%E6%84%8F%E6%80%9D%EF%BC%9F.php  參考文檔: https://www.php.net/manual/zh/features.gc.refcounting-basics.php https://www.jianshu.com/p/52450a61354d

本文轉(zhuǎn)載自:https://juejin.cn/post/6950093123682828301

作者:硬核項(xiàng)目經(jīng)理

推薦學(xué)習(xí):《PHP視頻教程》

贊(0)
分享到: 更多 (0)
網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)
成人精品一区二区电影| 国产精品99久久精品| 亚洲七七久久精品中文国产| 国产精品亚洲综合| 久久精品国产亚洲AV天海翼| 久久精品国产免费观看| 欧洲精品久久久av无码电影| 久久精品国产99国产电影网 | 精品哟哟哟国产在线观看不卡| 精品国产精品国产偷麻豆| 久久久久久亚洲Av无码精品专口| 国产成人精品白浆久久69| 自拍偷在线精品自拍偷无码专区 | 中文字幕精品无码久久久久久3D日动漫| 老司机福利精品视频| 日韩欧美亚洲中文乱码| 日韩精品无码中文字幕一区二区 | 日本精品一区二区三区在线视频| 日韩精品一区二区三区四区| 国产在线观看91精品一区| 国产69精品久久久久妇女| 国产成人无码aa精品一区| 国产精品久久免费视频| 国产精品亚洲天堂| 国产精品久久久久久久久kt| 国产精品亚洲а∨天堂2021| 国产精品WWW夜色视频| 国产精品久久久久久影视| 国产精品原创巨作av| 国产精品免费看久久久无码| 国产精品三级国语在线看| 日韩一区二区三区在线观看| 日韩内射美女人妻一区二区三区| 日韩免费的视频在线观看香蕉| 亚洲国产日韩a在线播放| 亚洲日韩一区二区三区| 日韩在线不卡视频| www亚洲精品少妇裸乳一区二区 | 在线精品视频播放| 精品人妻一区二区三区浪潮在线| 精品国产一区二区三区久久久狼|