誰(shuí)動(dòng)了我的內(nèi)存之PHP內(nèi)存溢出

-- 原創(chuàng)文章 轉(zhuǎn)載請(qǐng)注明出處

今天上午剛到公司,就有同事在公司群里反映某個(gè)計(jì)劃任務(wù)出現(xiàn)問題了。我就懷著刨根問底的心,去查看了log。發(fā)現(xiàn)挺有意思的一個(gè)問題,PHP內(nèi)存溢出導(dǎo)致腳本執(zhí)行失敗。那就一起來看個(gè)究竟吧!

  1. 首先查看了計(jì)劃任務(wù)的Log


    log_error.png
    log_error.png

從報(bào)錯(cuò)信息字面意思可以看出,允許的134217728 bytes的內(nèi)存已經(jīng)用盡,還要試圖分配12961640 bytes內(nèi)存。
給你(當(dāng)前腳本)分配的內(nèi)存你已經(jīng)用完了,你還想問系統(tǒng)要內(nèi)存。系統(tǒng)這時(shí)想對(duì)你說:

地主家也沒有余糧啊(借用葛優(yōu)大爺?shù)囊痪湓?

geyou.png
geyou.png
  1. 模擬一下"案發(fā)現(xiàn)場(chǎng)"
  • 新建一個(gè)mem_exhausted.php文件 copy過來一個(gè)2.4M的log文件做測(cè)試用


    log_size.png
    log_size.png
  • 寫個(gè)簡(jiǎn)單的腳本重現(xiàn)"案發(fā)現(xiàn)場(chǎng)" 故意分配1M的內(nèi)存 來讀取2.4M的log


    test_mem_1.png
    test_mem_1.png
  • 執(zhí)行腳本,"案發(fā)現(xiàn)場(chǎng)"重現(xiàn)


    test_run_res.png
    test_run_res.png
  1. 分析"事故"原因
    腳本一次性讀取了大量的數(shù)據(jù)(可能是讀的文件,可能是讀取的數(shù)據(jù)庫(kù))
    如下圖: 往杯子(分配給當(dāng)前腳本的內(nèi)存)里面倒數(shù)水(log文件的數(shù)據(jù)),杯子容量(內(nèi)存)不夠用

    water_overflow.jpg
    water_overflow.jpg

  2. 解決方案
    a. 既然杯子小 就換個(gè)大杯子(增大給腳本分配的內(nèi)存)治標(biāo)不治本: ini_set('memory_limit','100M');

    new_1.png
    new_1.png

    b. 把水分批次倒入杯子中(循環(huán),分段讀取數(shù)據(jù),讀數(shù)據(jù)庫(kù)的話可以用limit)

code_1.png
code_1.png

看看結(jié)果

run_res_new.png
run_res_new.png

分段讀取也是可以解決問題滴

  1. 其他優(yōu)化方案
  • 應(yīng)當(dāng)盡可能減少靜態(tài)變量的使用,在需要數(shù)據(jù)重用時(shí),可以考慮使用引用(&)。
  • 數(shù)據(jù)庫(kù)操作完成后,要馬上關(guān)閉連接;
  • 一個(gè)對(duì)象使用完,要及時(shí)調(diào)用析構(gòu)函數(shù)(__destruct())
  • 用過的變量及時(shí)銷毀(unset())掉
  • 可以使用memory_get_usage()函數(shù),獲取當(dāng)前占用內(nèi)存 根據(jù)當(dāng)前使用的內(nèi)存來調(diào)整程序
  • unset()函數(shù)只能在變量值占用內(nèi)存空間超過256字節(jié)時(shí)才會(huì)釋放內(nèi)存空間。(PHP內(nèi)核的gc垃圾回收機(jī)制決定)
  • 有當(dāng)指向該變量的所有變量(如引用變量)都被銷毀后,才會(huì)釋放內(nèi)存
    (PHP變量底層實(shí)現(xiàn)是一個(gè)_zval_struct結(jié)構(gòu)體,refcount_gc表示引用計(jì)數(shù) is_ref__gc表示是否為引用)

倉(cāng)促成文,不當(dāng)之處,在所難免,尚盼讀者,多加指正

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容