1. Go 內(nèi)存泄漏排查實(shí)戰(zhàn)

        共 1932字,需瀏覽 4分鐘

         ·

        2022-04-11 15:59

        例1:Goroutine 泄漏

        現(xiàn)象

        NumGoroutine 指標(biāo)持續(xù)上漲,且低峰期未下降,判斷出現(xiàn)了 Goroutine 泄漏現(xiàn)象。

        image.png

        排查

        1. 通過訪問線上服務(wù) pprof 暴露出來的 HTTP 接口,拿到當(dāng)前所有協(xié)程的堆棧信息;curl http://「ip:port」/debug/pprof/goroutine?debug=2

        2. 發(fā)現(xiàn)存在大量存活時間超過上千分鐘的 Goroutine,觀察堆棧疑似是 http 連接未釋放導(dǎo)致,通過對下圖 net.sockets.tcp.inuse(正在使用的tcp socket數(shù)量)指標(biāo)的觀察進(jìn)行了進(jìn)一步的確認(rèn);

        結(jié)論

        http

        下面以本次 case http 服務(wù)為例,做簡單介紹:

        • 上游服務(wù)作為客戶端使用了 http1.1 并且將連接設(shè)置為 keepalive;
        • 本服務(wù)作為服務(wù)端未設(shè)置 idletimeout 與 readtimeout;

        當(dāng)這兩種情況同時發(fā)生時,如果上游持有對本服務(wù)的連接不進(jìn)行釋放,那么服務(wù)端會一直維持這個連接的存在,不進(jìn)行回收,進(jìn)而導(dǎo)致協(xié)程泄漏;

        client上游客戶端可能為 GO、Java 等,以下為 GO 語言 http 客戶端的空閑連接超時設(shè)置;server解決建議啟動 http server 盡量用后者,前者雖然簡單,但是服務(wù)不夠健壯;

        thrift

        server

        image.png

        Tips

        需要注意的一點(diǎn)是,這個 Goroutine 泄漏問題不止在 http 下會發(fā)生,在 thrift、grpc 中也是同樣的道理,如果服務(wù)端不對連接設(shè)置 timeout,某些情況下就會被上游拖死。

        Reference

        一起 goroutine 泄漏問題的排查[1]

        例2:內(nèi)存居高不下

        現(xiàn)象

        內(nèi)存使用量(mem.rss)居高不下,且低峰期未下降,懷疑發(fā)生了內(nèi)存泄漏現(xiàn)象;

        排查

        1. 剛開始懷疑時內(nèi)存泄漏,但是抓取 pprof heap 圖觀察后,未發(fā)現(xiàn)泄露問題,且內(nèi)存分配符合預(yù)期;
        2. 發(fā)現(xiàn)內(nèi)存使用雖然居高不下,但未呈上漲趨勢,因此修改關(guān)鍵字為“go 內(nèi)存占用居高不下”,發(fā)現(xiàn)有相同問題;

        結(jié)論

        問題來自于 GO 在將內(nèi)存歸還給操作系統(tǒng)時的內(nèi)存釋放策略,詳情見官方 issues[2],以下做簡單介紹。

        GO 內(nèi)存釋放策略

        (此節(jié)內(nèi)容整理自 壓測后go服務(wù)內(nèi)存暴漲[3]

        不同策略的釋放機(jī)制

        • MADV_DONTNEED:內(nèi)核將會在合適的時機(jī)去釋放內(nèi)存,但進(jìn)程的 RSS(常駐內(nèi)存)將會立即減少。如果再次申請內(nèi)存,內(nèi)核會重新分配一塊新的空間。
        • MADV_FREE:只能在 linux 內(nèi)核版本 4.5 以上才能使用,此操作理論上只是打了一個標(biāo)記位,只有在內(nèi)核感覺到內(nèi)存壓力的時候才會將這些打標(biāo)記的內(nèi)存回收掉,分配給其他進(jìn)程使用。這個策略下進(jìn)程的 RSS 不會立即減少。

        不同策略的實(shí)際差別

        • 理論上 MADV_FREE 效率要高一些,通過在頁表中做標(biāo)記的方式,延遲內(nèi)存的分配和回收,可以提高內(nèi)存管理的效率,畢竟內(nèi)存的回收和分配都是會消耗系統(tǒng)性能的;
        • 導(dǎo)致的 RSS 指標(biāo)變化 MADV_DONTNEED 會導(dǎo)致進(jìn)程 RSS 會有明顯的下降;MADV_FREE 會導(dǎo)致進(jìn)程 RSS 平穩(wěn)在高峰,不會得到立即釋放;

        不同 GO 版本的釋放策略

        • 在 GO1.12 之前,默認(rèn)均選擇的 MADV_DONTNEED 策略進(jìn)行內(nèi)存回收;
        • 在 GO1.12~GO1.15,官方默認(rèn)選擇 MADV_FREE 策略進(jìn)行內(nèi)存回收;
        • 在 GO1.16 及之后,又改回了 MADV_DONTNEED 策略進(jìn)行回收內(nèi)存。

        在 GO1.12~GO1.15 且內(nèi)核版本 4.5 以上,mem.rss 指標(biāo)已經(jīng)無法準(zhǔn)確觀測服務(wù)內(nèi)存占用;

        解決方法

        • 不解決,對程序性能有利,但是會降低一些可觀測性;
        • 以下任一方法可以解決,但會損失一定性能 把 export GODEBUG=madvdontneed=1 寫進(jìn)服務(wù) control.sh 腳本;
        • 升級 GO 版本至 1.16 及以上;

        參考資料

        [1]

        一起 goroutine 泄漏問題的排查: https://zhuanlan.zhihu.com/p/100740270

        [2]

        issues: https://github.com/golang/go/issues/42330

        [3]

        壓測后go服務(wù)內(nèi)存暴漲: http://soiiy.com/go/17114.html



        推薦閱讀


        福利

        我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號 「polarisxu」,回復(fù)?ebook?獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

        瀏覽 47
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
          
          

            1. 日本天天综合网 | 国产精品嫩草影院 | 午夜精品导航 | 亚洲成人a | 动漫美女被吸乳jk白丝91 |