PHP的內(nèi)存泄露問題與垃圾回收

你寫了一個(gè)php腳本摇天,一般都不用考慮內(nèi)存泄露和垃圾回收的問題硫眯,因?yàn)橐话闱闆r下你的腳本很快就執(zhí)行完退出了。
但在一些運(yùn)行時(shí)間長井赌,數(shù)據(jù)量大的時(shí)候谤逼,程序運(yùn)行一段時(shí)間后,php腳本就占用了過多內(nèi)存仇穗,然后就報(bào)錯(cuò)(PHP Fatal error: Allowed memory size of 134217728 bytes exhausted)退出了流部。一般來說,每個(gè)頁面處理結(jié)束纹坐,新建的simple_html_dom對(duì)象就應(yīng)該被銷毀了——但是實(shí)際上沒有枝冀,很明顯,內(nèi)存泄露發(fā)生了耘子。
PHP的垃圾回收機(jī)制
php 5.3之前使用的垃圾回收機(jī)制是單純的“引用計(jì)數(shù)”果漾,也就是每個(gè)內(nèi)存對(duì)象都分配一個(gè)計(jì)數(shù)器,當(dāng)內(nèi)存對(duì)象被變量引用時(shí)谷誓,計(jì)數(shù)器+1绒障;當(dāng)變量引用撤掉后,計(jì)數(shù)器-1捍歪;當(dāng)計(jì)數(shù)器=0時(shí)户辱,表明內(nèi)存對(duì)象沒有被使用鸵钝,該內(nèi)存對(duì)象則進(jìn)行銷毀,垃圾回收完成庐镐。
“引用計(jì)數(shù)”存在問題恩商,就是當(dāng)兩個(gè)或多個(gè)對(duì)象互相引用形成環(huán)狀后,內(nèi)存對(duì)象的計(jì)數(shù)器則不會(huì)消減為0必逆;這時(shí)候怠堪,這一組內(nèi)存對(duì)象已經(jīng)沒用了,但是不能回收名眉,從而導(dǎo)致內(nèi)存泄露粟矿。
php5.3開始,使用了新的垃圾回收機(jī)制璧针,在引用計(jì)數(shù)基礎(chǔ)上嚷炉,實(shí)現(xiàn)了一種復(fù)雜的算法,來檢測內(nèi)存對(duì)象中引用環(huán)的存在探橱,以避免內(nèi)存泄露。
查看內(nèi)存是否泄露
看是否有該釋放的內(nèi)存沒有被釋放绘证,可以簡單的通過 調(diào)用 memory_get_usage 函數(shù)查看內(nèi)存使用情況來判斷隧膏;memory_get_usage 函數(shù)返回的內(nèi)存使用數(shù)據(jù)據(jù)說不是很準(zhǔn)確,可以使用 php 的 xdebug 擴(kuò)展來獲得更準(zhǔn)確翔實(shí)的內(nèi)存使用情況嚷那。

class A{
    private $b;
    function __construct(){
        $this->b = new B($this);
    }
    function __destruct(){
        //echo "A destruct\n";
    }
}
class B{
    private $a;
    function __construct($a){
        $this->a = $a;
    }
    function __destruct(){
        //echo "B descturct\n";
    }
}
for($i=0;;$i++){
    $a = new A();
    if($i00 == 0){
        echo memory_get_usage()."\n";
    }
}

上面就構(gòu)造了一個(gè)會(huì)產(chǎn)生環(huán)狀引用的例子胞枕。每次創(chuàng)建一個(gè)A對(duì)象的實(shí)例a,a就創(chuàng)建一個(gè)B對(duì)象的實(shí)例b魏宽,同時(shí)讓b引用a腐泻。這樣,每個(gè)A對(duì)象永遠(yuǎn)被一個(gè)B引用队询,而每個(gè)B對(duì)象同時(shí)被一個(gè)對(duì)象A引用派桩,引用環(huán)就這樣產(chǎn)生了。

在php5.2的環(huán)境下執(zhí)行這段代碼蚌斩,會(huì)發(fā)現(xiàn)內(nèi)存使用在單調(diào)上漲铆惑,也沒有A和B的析構(gòu)函數(shù)被執(zhí)行后輸出的“A/B desctruct”信息;直到內(nèi)存耗盡送膳,輸出“PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 40 bytes)”员魏。

在php5.3的環(huán)境下執(zhí)行這段代碼,則發(fā)現(xiàn)內(nèi)存使用在上跳下竄叠聋,但是永遠(yuǎn)沒有超過一個(gè)限額撕阎。程序也會(huì)輸出大量的“A/B desctruct”,這說明析構(gòu)函數(shù)被調(diào)用了碌补。
我的同事的程序中虏束,就存在這種引用的環(huán)路名斟,而他的腳本,實(shí)在php5.2.3下執(zhí)行的魄眉。simple_html_dom工具中砰盐,有兩個(gè)類,分別是simple_html_dom和simple_html_dom_node坑律,前者中有一個(gè)數(shù)組成員變量nodes岩梳,數(shù)組中每個(gè)元素都是一個(gè)simple_html_dom_node對(duì)象;而每個(gè)simple_html_dom_node對(duì)象都有一個(gè)成員變量dom晃择,該dom的值就是前面的simple_html_dom對(duì)象——這樣就形成了一個(gè)漂亮的引用環(huán)冀值,導(dǎo)致了內(nèi)存泄露。解決的辦法也很簡單宫屠,就是simple_html_dom對(duì)象在使用完畢時(shí)列疗,主動(dòng)調(diào)用其clear函數(shù),清空其成員變量nodes浪蹂,環(huán)就被打破了抵栈,內(nèi)存泄露也就不會(huì)發(fā)生了。

其他

  1. 垃圾回收的時(shí)機(jī)
    PHP中坤次,引用計(jì)數(shù)為0古劲,則內(nèi)存立刻釋放。也就是說缰猴,不存在環(huán)狀引用的變量产艾,離開變量的作用域,內(nèi)存被立刻釋放滑绒。環(huán)狀引用檢測則是在滿足一定條件下觸發(fā)闷堡,所以在上面的例子中,會(huì)看到使用的內(nèi)存有大幅度的波動(dòng)疑故。也可以通過 gc_collect_cycles 函數(shù)來主動(dòng)進(jìn)行環(huán)狀引用檢測杠览。
  2. &符號(hào)的影響
    顯式引用一個(gè)變量,會(huì)增加該內(nèi)存的引用計(jì)數(shù):
$a = "something";
$b = &$a;

此時(shí)unset($a), 但是仍有$b指向該內(nèi)存區(qū)域的引用焰扳,內(nèi)存不會(huì)釋放倦零。

  1. unset函數(shù)的影響
    unset只是斷開一個(gè)變量到一塊內(nèi)存區(qū)域的連接,同時(shí)將該內(nèi)存區(qū)域的引用計(jì)數(shù)-1吨悍;在上面的例子中扫茅,循環(huán)體內(nèi)部,$a=new A(); unset($a);并不會(huì)將$a的引用計(jì)數(shù)減到零育瓜;
  2. = null 操作的影響葫隙;
    $a = null 是直接將$a 指向的數(shù)據(jù)結(jié)構(gòu)置空,同時(shí)將其引用計(jì)數(shù)歸0躏仇。
  3. 腳本執(zhí)行結(jié)束的影響
    腳本執(zhí)行結(jié)束恋脚,該腳本中使用的所有內(nèi)存都會(huì)被釋放腺办,不論是否有引用環(huán)。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末糟描,一起剝皮案震驚了整個(gè)濱河市怀喉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌船响,老刑警劉巖躬拢,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異见间,居然都是意外死亡聊闯,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門米诉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來菱蔬,“玉大人,你說我怎么就攤上這事史侣∷┟冢” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵抵窒,是天一觀的道長弛针。 經(jīng)常有香客問我,道長李皇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任宙枷,我火速辦了婚禮掉房,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘慰丛。我一直安慰自己卓囚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布诅病。 她就那樣靜靜地躺著哪亿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪贤笆。 梳的紋絲不亂的頭發(fā)上蝇棉,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音芥永,去河邊找鬼篡殷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛埋涧,可吹牛的內(nèi)容都是我干的板辽。 我是一名探鬼主播奇瘦,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼劲弦!你這毒婦竟也來了耳标?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤邑跪,失蹤者是張志新(化名)和其女友劉穎次坡,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呀袱,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贸毕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了夜赵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片明棍。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖寇僧,靈堂內(nèi)的尸體忽然破棺而出摊腋,到底是詐尸還是另有隱情,我是刑警寧澤嘁傀,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布兴蒸,位于F島的核電站,受9級(jí)特大地震影響细办,放射性物質(zhì)發(fā)生泄漏橙凳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一笑撞、第九天 我趴在偏房一處隱蔽的房頂上張望岛啸。 院中可真熱鬧,春花似錦茴肥、人聲如沸坚踩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瞬铸。三九已至,卻和暖如春础锐,著一層夾襖步出監(jiān)牢的瞬間嗓节,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來泰國打工郁稍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赦政,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像恢着,于是被迫代替她去往敵國和親桐愉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

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