PHP gc機(jī)制

在理解PHP垃圾回收機(jī)制(GC)之前,先了解一下變量的存儲(chǔ)。
php中變量存在于一個(gè)zval的變量容器中食铐。結(jié)構(gòu)如下:


php變量容器示意圖

zval中,除了存儲(chǔ)變量的類(lèi)型和值之外僧鲁,還有is_ref字段和refcount字段虐呻。

  • is_ref:是個(gè)bool值象泵,用來(lái)區(qū)分變量是否屬于引用集合。什么意思呢斟叼,你可以這么認(rèn)為:表示變量是否有一個(gè)以上的別名偶惠。
  • refcount:計(jì)數(shù)器,表示指向這個(gè)zval變量容器的變量個(gè)數(shù)朗涩。
    兩者之間有這么一個(gè)默認(rèn)關(guān)系:當(dāng)refcount值為1時(shí)洲鸠,is_ref的值為false。因?yàn)閞efcount為1馋缅,此變量不可能有多個(gè)別名,也就不存在引用了绢淀。
<?php
$a = 1;
xdebug_debug_zval('a');
echo PHP_EOL;
$b = $a;
xdebug_debug_zval('a');
echo PHP_EOL;
 
$c = &$a;
xdebug_debug_zval('a');
echo PHP_EOL;
 
xdebug_debug_zval('b');
echo PHP_EOL;
?>

運(yùn)行結(jié)果如下:
a:(refcount=1, is_ref=0),int 1

a:(refcount=2, is_ref=0),int 1

a:(refcount=2, is_ref=1),int 1

b:(refcount=1, is_ref=0),int 1

上面描述的zval存儲(chǔ)的是標(biāo)量萤悴,那復(fù)合類(lèi)型的數(shù)組是如何存儲(chǔ)的呢?

<?php
$a = array( 'meaning' => 'life', 'number' => 42 );
xdebug_debug_zval( 'a' );
echo PHP_EOL;
class Test{
    public $a = 1;
    public $b = 2;
     
    function handle(){
        echo 'hehe';
    }
}
 
$test = new Test();
xdebug_debug_zval('test');
?>

運(yùn)行結(jié)果如下:

a:(refcount=1, is_ref=0),

array
  'meaning' => (refcount=1, is_ref=0),
string

'life' (length=4)
  'number' => (refcount=1, is_ref=0),
int

 42
test:(refcount=1, is_ref=0),

object(Test)[1]
  public 'a' => (refcount=2, is_ref=0),
int

 1
  public 'b' => (refcount=2, is_ref=0),
int

2

可以看出皆的,數(shù)組用了比數(shù)組長(zhǎng)度多1個(gè)zval存儲(chǔ)覆履。對(duì)象類(lèi)似。下面給出了數(shù)組的存儲(chǔ)形象表示

php復(fù)合類(lèi)型zval結(jié)構(gòu)示意圖

可以看到:數(shù)組分配了三個(gè)zval容器:a meaning number

現(xiàn)在看看所謂的環(huán)狀引用是如何生成的

<?php
$a = array( 'one' );
$a[] =& $a;
xdebug_debug_zval( 'a' );
?>

運(yùn)行結(jié)果:

a:(refcount=2, is_ref=1),

array
  0 => (refcount=1, is_ref=0),
string

 'one' (length=3)
  1 => (refcount=2, is_ref=1), &array

a 和 1 的zval容器 是一樣的费薄。如下:

image.png

這樣就形成了環(huán)狀引用硝全。

在5.2及更早版本的PHP中,沒(méi)有專(zhuān)門(mén)的垃圾回收器GC(Garbage Collection)楞抡,引擎在判斷一個(gè)變量空間是否能夠被釋放的時(shí)候是依據(jù)這個(gè)變量的zval的refcount的值伟众,如果refcount為0,那么變量的空間可以被釋放召廷,否則就不釋放凳厢,這是一種非常簡(jiǎn)單的GC實(shí)現(xiàn)。

現(xiàn)在unset ($a),那么array的refcount減1變?yōu)?.現(xiàn)在無(wú)任何變量指向這個(gè)zval竞慢,而且這個(gè)zval的計(jì)數(shù)器為1先紫,不會(huì)回收。


image.png

盡管不再有某個(gè)作用域中的任何符號(hào)指向這個(gè)結(jié)構(gòu)(就是變量容器)筹煮,由于數(shù)組元素“1”仍然指向數(shù)組本身遮精,所以這個(gè)容器不能被清除 。因?yàn)闆](méi)有另外的符號(hào)指向它败潦,用戶沒(méi)有辦法清除這個(gè)結(jié)構(gòu)本冲,結(jié)果就會(huì)導(dǎo)致內(nèi)存泄漏。慶幸的是变屁,php將在請(qǐng)求結(jié)束時(shí)清除這個(gè)數(shù)據(jù)結(jié)構(gòu)眼俊,但是在php清除之前,將耗費(fèi)不少空間的內(nèi)存粟关。如果你要實(shí)現(xiàn)分析算法疮胖,或者要做其他像一個(gè)子元素指向它的父元素這樣的事情环戈,這種情況就會(huì)經(jīng)常發(fā)生。當(dāng)然澎灸,同樣的情況也會(huì)發(fā)生在對(duì)象上院塞,實(shí)際上對(duì)象更有可能出現(xiàn)這種情況,因?yàn)閷?duì)象總是隱式的被引用性昭。

如果上面的情況發(fā)生僅僅一兩次倒沒(méi)什么拦止,但是如果出現(xiàn)幾千次,甚至幾十萬(wàn)次的內(nèi)存泄漏糜颠,這顯然是個(gè)大問(wèn)題汹族。在長(zhǎng)時(shí)間運(yùn)行的腳本,比如請(qǐng)求基本上不會(huì)結(jié)束的守護(hù)進(jìn)程時(shí)其兴,就會(huì)出現(xiàn)問(wèn)題顶瞒,內(nèi)存空間會(huì)不斷耗費(fèi),導(dǎo)致內(nèi)存不足而崩潰元旬。

PHP5.3中榴徐,采用了專(zhuān)門(mén)的算法(比較復(fù)雜)。匀归,來(lái)處理環(huán)狀引用導(dǎo)致內(nèi)存泄露的問(wèn)題坑资。

當(dāng)一個(gè)zval可能為垃圾時(shí),回收算法會(huì)把這個(gè)zval放入一個(gè)內(nèi)存緩沖區(qū)穆端。當(dāng)緩沖區(qū)達(dá)到最大臨界值時(shí)(最大值可以設(shè)置)袱贮,回收算法會(huì)循環(huán)遍歷所有緩沖區(qū)中的zval,判斷其是否為垃圾体啰,并進(jìn)行釋放處理字柠。或者我們?cè)谀_本中使用gc_collect_cycles,強(qiáng)制回收緩沖區(qū)中的垃圾狡赐。

在php5.3的GC中窑业,針對(duì)的垃圾做了如下說(shuō)明:

1:如果一個(gè)zval的refcount增加,那么此zval還在使用枕屉,肯定不是垃圾常柄,不會(huì)進(jìn)入緩沖區(qū)

2:如果一個(gè)zval的refcount減少到0, 那么zval會(huì)被立即釋放掉搀擂,不屬于GC要處理的垃圾對(duì)象西潘,不會(huì)進(jìn)入緩沖區(qū)。

3:如果一個(gè)zval的refcount減少之后大于0哨颂,那么此zval還不能被釋放喷市,此zval可能成為一個(gè)垃圾,將其放入緩沖區(qū)威恼。PHP5.3中的GC針對(duì)的就是這種zval進(jìn)行的處理品姓。

開(kāi)啟垃圾回收機(jī)制后寝并,針對(duì)內(nèi)存泄露的情況,可以節(jié)省大量的內(nèi)存空間腹备,但是由于垃圾回收算法運(yùn)行耗費(fèi)時(shí)間衬潦,開(kāi)啟垃圾回收算法會(huì)增加腳本的執(zhí)行時(shí)間。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末植酥,一起剝皮案震驚了整個(gè)濱河市镀岛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌友驮,老刑警劉巖漂羊,帶你破解...
    沈念sama閱讀 222,464評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異卸留,居然都是意外死亡拨与,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)艾猜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人捻悯,你說(shuō)我怎么就攤上這事匆赃。” “怎么了今缚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,078評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵算柳,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我姓言,道長(zhǎng)瞬项,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,979評(píng)論 1 299
  • 正文 為了忘掉前任何荚,我火速辦了婚禮囱淋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘餐塘。我一直安慰自己妥衣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布戒傻。 她就那樣靜靜地躺著税手,像睡著了一般。 火紅的嫁衣襯著肌膚如雪需纳。 梳的紋絲不亂的頭發(fā)上芦倒,一...
    開(kāi)封第一講書(shū)人閱讀 52,584評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音不翩,去河邊找鬼兵扬。 笑死麻裳,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的周霉。 我是一名探鬼主播掂器,決...
    沈念sama閱讀 41,085評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼俱箱!你這毒婦竟也來(lái)了国瓮?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,023評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤狞谱,失蹤者是張志新(化名)和其女友劉穎乃摹,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體跟衅,經(jīng)...
    沈念sama閱讀 46,555評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡孵睬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了伶跷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掰读。...
    茶點(diǎn)故事閱讀 40,769評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖叭莫,靈堂內(nèi)的尸體忽然破棺而出蹈集,到底是詐尸還是另有隱情,我是刑警寧澤雇初,帶...
    沈念sama閱讀 36,439評(píng)論 5 351
  • 正文 年R本政府宣布拢肆,位于F島的核電站,受9級(jí)特大地震影響靖诗,放射性物質(zhì)發(fā)生泄漏郭怪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評(píng)論 3 335
  • 文/蒙蒙 一刊橘、第九天 我趴在偏房一處隱蔽的房頂上張望鄙才。 院中可真熱鬧,春花似錦促绵、人聲如沸咒循。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,601評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)叙甸。三九已至,卻和暖如春位衩,著一層夾襖步出監(jiān)牢的瞬間裆蒸,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,702評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工糖驴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留僚祷,地道東北人佛致。 一個(gè)月前我還...
    沈念sama閱讀 49,191評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像辙谜,于是被迫代替她去往敵國(guó)和親俺榆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 前言 PHP5.3之前(不包括5.3)的垃圾回收機(jī)制装哆,是沒(méi)有專(zhuān)門(mén)的垃圾回收器罐脊。只是簡(jiǎn)單的判斷了一下變量的zval的...
    PHP的點(diǎn)滴閱讀 425評(píng)論 0 1
  • 每個(gè)php變量存在一個(gè)叫"zval"的變量容器中。包括四個(gè)字段:-name:字段值蜕琴,-type:字段類(lèi)型萍桌,-Is_...
    木有sky閱讀 1,557評(píng)論 0 2
  • 簡(jiǎn)介 如果自己實(shí)現(xiàn)一個(gè)自動(dòng) GC,最簡(jiǎn)單的方式:在函數(shù)定義變量時(shí)分配一塊內(nèi)存凌简,用于保存 zval 及對(duì)應(yīng)的 val...
    淡淡de鹽閱讀 439評(píng)論 0 0
  • 每一種語(yǔ)言都有自己的自動(dòng)垃圾回收機(jī)制上炎,讓程序員不必過(guò)分關(guān)心程序內(nèi)存分配,但是在OOP中雏搂,有些對(duì)象需要顯式的銷(xiāo)毀藕施;防...
    文檔隨手記閱讀 4,755評(píng)論 2 3
  • 在了解垃圾回收機(jī)制之前我們必須先來(lái)看下zval結(jié)構(gòu)。zval是用于保存變量以及常量的凸郑。 php5中的zval數(shù)據(jù)結(jié)...
    十年磨一劍1111閱讀 367評(píng)論 0 1