成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

【PHP源碼學(xué)習(xí)】2019-03-19 PHP引用

Aomine / 1299人閱讀

摘要:中的引用引用可以通過不同的變量名,訪問同一個(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

相關(guān)文章

  • 【LNMPR源碼學(xué)習(xí)】筆記匯總

    摘要:此文用于匯總跟隨陳雷老師及團(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...

    Barrior 評(píng)論0 收藏0
  • PHP源碼學(xué)習(xí)2019-04-01 PHP垃圾回收1

    摘要:以上詳細(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)回...

    Leo_chen 評(píng)論0 收藏0
  • PHP源碼學(xué)習(xí)2019-03-12 PHP基本變量筆記

    摘要:中以表示所有的變量,它是一個(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ì)上就是給一段...

    Fundebug 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<