摘要:中的引用引用可以通過不同的變量名,訪問同一個(gè)變量?jī)?nèi)容。在進(jìn)行引用賦值后,等號(hào)左右兩邊的變量均變成了引用類型。緩沖區(qū)的作用就是減少垃圾回收算法運(yùn)行的頻率,減少對(duì)正在運(yùn)行的服務(wù)端代碼的影響。
baiyan
全部視頻:https://segmentfault.com/a/11...
原視頻地址:http://replay.xesv5.com/ll/26...
由于這個(gè)系列的視頻后面會(huì)再次細(xì)講垃圾回收,那么我們今天先復(fù)習(xí)一下PHP中的引用,為后面做一個(gè)鋪墊,后續(xù)的筆記會(huì)詳細(xì)講解垃圾回收器的相關(guān)運(yùn)行原理。
PHP7中的引用引用:可以通過不同的變量名,訪問同一個(gè)變量?jī)?nèi)容。
PHP7中的引用通過讓兩個(gè)變量指向同一塊內(nèi)存空間實(shí)現(xiàn)了上述特性。在進(jìn)行引用賦值后,等號(hào)左右兩邊的變量均變成了引用類型(IS_REFERENCE)。這塊公用的內(nèi)存空間就是PHP7為引用類型的變量專門創(chuàng)建的一個(gè)結(jié)構(gòu)體,叫做zend_reference。
代碼示例:
$a = 1; echo $a; $b = &$a; //$b是$a的引用 echo $a; echo $b; unset($b); echo $a;
我們用gdb調(diào)試以上代碼:
首先執(zhí)行$a = 1;并且打印$a的值,$a就是一個(gè)普通的zval,其類型是IS_LONG,很好理解:
執(zhí)行關(guān)鍵的一步:$b = &$a,打印$a的值,觀察$a的存儲(chǔ)情況:
觀察上圖,可以發(fā)現(xiàn)$a的type變成了10 (IS_REFERENCE)類型,并且ref字段指向了一個(gè)新的結(jié)構(gòu)體,這就是zend_reference,zend_reference中存儲(chǔ)著$a與$b共同的值1,由于$a與$b同時(shí)引用著這個(gè)結(jié)構(gòu)體,故此時(shí)該結(jié)構(gòu)體的refcount = 2。
接下來打印$b,觀察$b的存儲(chǔ)情況:
觀察上圖,發(fā)現(xiàn)與$b的type也是IS_REFERENCE類型,且ref字段也指向了一個(gè)zend_reference結(jié)構(gòu)體,比較$a與$b指向的zend_reference,二者地址相同,說明指向了同一個(gè)zend_reference結(jié)構(gòu)體。此時(shí)兩個(gè)變量的存儲(chǔ)情況如下圖所示:
接下來執(zhí)行unset($b),觀察$a以及zend_reference的存儲(chǔ)情況,我們看是否符合預(yù)期:
我們看到unset($b)之后,$a所指向的zend_reference的refcount由2變?yōu)?,說明現(xiàn)在只有$a引用著這個(gè)結(jié)構(gòu)體,b不再引用這個(gè)結(jié)構(gòu)體,其類型變成了IS_UNDEF類型,代碼執(zhí)行完畢。
那么我們看一下zend_reference結(jié)構(gòu)體的基本結(jié)構(gòu):
struct _zend_reference { zend_refcounted_h gc; //gc相關(guān),存有refcount zval val; //引用類型的變量值存在這個(gè)zval中的zend_value字段中。簡(jiǎn)單類型的值直接存在這里,復(fù)雜類型的值存儲(chǔ)對(duì)應(yīng)數(shù)據(jù)結(jié)構(gòu)的指針,來找到這個(gè)變量的值,和之前講基本變量時(shí)候講過的一樣。 };
這個(gè)結(jié)構(gòu)體一共只有2個(gè)字段,gc字段中是zend_refcounted_gc結(jié)構(gòu)體類型,其中存儲(chǔ)了引用計(jì)數(shù);val字段存儲(chǔ)了引用類型變量的值(簡(jiǎn)單類型如整型、浮點(diǎn)型的值直接存在這里,復(fù)雜類型存對(duì)應(yīng)數(shù)據(jù)結(jié)構(gòu)的指針,與之前講基本變量的時(shí)候講過的一樣)。這樣相當(dāng)于加了一個(gè)中間層,使得原始的zend_string或zend_array在內(nèi)存中只有1份,方便管理與維護(hù)。
循環(huán)引用問題我們首先構(gòu)造一個(gè)循環(huán)引用:
time()]; echo $a; $a[] = &$a; //循環(huán)引用 echo $a; unset($a); echo $a;
注意:由于開啟opcache的PHP7會(huì)在數(shù)組初始化的元素全部為常量元素的時(shí)候,將其優(yōu)化成不可變數(shù)組(immutable array),這里的引用計(jì)數(shù)值refcount = 2只是一個(gè)偽引用計(jì)數(shù),所以我們使用$a = ["time" => time()],讓其初始化后的refcount為正常的1。]見下圖:
利用gdb調(diào)試這段代碼:
執(zhí)行完$a初始化并打印$a,refcount為1,type為7(IS_ARRAY)而此時(shí)的ref字段中的值是非法地址,說明此時(shí)還沒有生成中間的zend_reference結(jié)構(gòu)體:
繼續(xù)執(zhí)行下一行$a[] = &$a; 觀察下圖中綠色方框的含義:
- $a的zval中的ref指向zend_reference結(jié)構(gòu)體 - zend_reference結(jié)構(gòu)體中的zval字段中的arr指針指向了原始的zend_array - zend_array中的arData指針指向了bucket類型 - zend_array中的bucket數(shù)組元素也是一個(gè)IS_REFERENCE類型,它又指回到同一個(gè)zend_reference結(jié)構(gòu)體:
根據(jù)gdb調(diào)試情況畫出內(nèi)存結(jié)構(gòu)圖:
由于有兩個(gè)東西指向zend_reference結(jié)構(gòu)體(一個(gè)是$a,一個(gè)是$a數(shù)組中的一個(gè)元素),所以refcount = 2。原始的zend_array中也有一個(gè)refcount字段,由于只有一個(gè)zend_reference指向這個(gè)zend_array,所以refcount = 1。
接下來繼續(xù)執(zhí)行unset($a):
我們可以看到,$a的type類型變成了0(IS_UNDEF),同時(shí)其指向的zend_reference結(jié)構(gòu)體的refcount變?yōu)榱?(因?yàn)?a數(shù)組中的元素仍然在指向它),我們畫圖來表示一下現(xiàn)在的內(nèi)存情況:
那么問題出現(xiàn)了,$a是unset掉了,但是由于原始的zend_array中的元素仍然在指向仍然在指向zend_reference結(jié)構(gòu)體,所以zend_reference的refcount是1,而并非是預(yù)期的0。這樣一來,這兩個(gè)zend_reference與zend_array結(jié)構(gòu)在unset($a)之后,仍然存在于內(nèi)存之中,如果對(duì)此不作任何處理,就會(huì)造成內(nèi)存泄漏。
那么如何解決循環(huán)引用帶來的內(nèi)存泄漏問題呢?垃圾回收就要派上用場(chǎng)了。在PHP7中,如果檢測(cè)到refcount -1 后仍 > 0的變量,會(huì)把它放入一個(gè)雙向鏈表中,等待垃圾回收,相當(dāng)于一個(gè)緩沖區(qū)的作用。待緩沖區(qū)滿了之后(10000個(gè)存儲(chǔ)單元),然后再對(duì)其進(jìn)行標(biāo)記和清除(以后會(huì)在代碼層面具體講垃圾回收的方法)。
緩沖區(qū)的作用就是減少垃圾回收算法運(yùn)行的頻率,減少對(duì)正在運(yùn)行的服務(wù)端代碼的影響。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/31313.html
摘要:此文用于匯總跟隨陳雷老師及團(tuán)隊(duì)的視頻,學(xué)習(xí)源碼過程中的思考整理與心得體會(huì),此文會(huì)不斷更新視頻傳送門每日學(xué)習(xí)記錄使用錄像設(shè)備記錄每天的學(xué)習(xí)源碼學(xué)習(xí)源碼學(xué)習(xí)內(nèi)存管理筆記源碼學(xué)習(xí)內(nèi)存管理筆記源碼學(xué)習(xí)內(nèi)存管理筆記源碼學(xué)習(xí)基本變量筆記 此文用于匯總跟隨陳雷老師及團(tuán)隊(duì)的視頻,學(xué)習(xí)源碼過程中的思考、整理與心得體會(huì),此文會(huì)不斷更新 視頻傳送門:【每日學(xué)習(xí)記錄】使用錄像設(shè)備記錄每天的學(xué)習(xí) PHP7...
摘要:以上詳細(xì)的講解請(qǐng)看源碼學(xué)習(xí)引用那么如何解決循環(huán)引用帶來的內(nèi)存泄漏問題呢我們的垃圾回收就要派上用場(chǎng)了。接下來判斷如果垃圾回收器已經(jīng)運(yùn)行,那么本次就不再執(zhí)行了。 baiyan 全部視頻:https://segmentfault.com/a/11... 垃圾回收觸發(fā)條件 我們知道,在PHP中,如果一個(gè)變量的引用計(jì)數(shù)減少到0(沒有任何地方在使用這個(gè)變量),它所占用的內(nèi)存就會(huì)被PHP虛擬機(jī)自動(dòng)回...
摘要:中以表示所有的變量,它是一個(gè)結(jié)構(gòu)體。類型是一個(gè)聯(lián)合體,共占用。字段表示字符串的哈希值,在數(shù)組的中有用,方便快速定位。字段表示字符串長(zhǎng)度這里就是一個(gè)柔性數(shù)組,在等源碼中也被大量使用。 baiyan 全部視頻:https://segmentfault.com/a/11... 源視頻地址:http://replay.xesv5.com/ll/24... 引入及基本概念 變量本質(zhì)上就是給一段...
閱讀 3912·2021-11-17 09:33
閱讀 1213·2021-10-09 09:44
閱讀 412·2019-08-30 13:59
閱讀 3487·2019-08-30 11:26
閱讀 2190·2019-08-29 16:56
閱讀 2862·2019-08-29 14:22
閱讀 3157·2019-08-29 12:11
閱讀 1283·2019-08-29 10:58