Go 內(nèi)存泄漏排查實戰(zhàn)
現(xiàn)象
NumGoroutine 指標持續(xù)上漲,且低峰期未下降,判斷出現(xiàn)了 Goroutine 泄漏現(xiàn)象。

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

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

結(jié)論
http
下面以本次 case http 服務為例,做簡單介紹:
上游服務作為客戶端使用了 http1.1 并且將連接設置為 keepalive; 本服務作為服務端未設置 idletimeout 與 readtimeout;
當這兩種情況同時發(fā)生時,如果上游持有對本服務的連接不進行釋放,那么服務端會一直維持這個連接的存在,不進行回收,進而導致協(xié)程泄漏;
client上游客戶端可能為 GO、Java 等,以下為 GO 語言 http 客戶端的空閑連接超時設置;
server
解決
建議啟動 http server 盡量用后者,前者雖然簡單,但是服務不夠健壯;
thrift
server

Tips
需要注意的一點是,這個 Goroutine 泄漏問題不止在 http 下會發(fā)生,在 thrift、grpc 中也是同樣的道理,如果服務端不對連接設置 timeout,某些情況下就會被上游拖死。
Reference
一起 goroutine 泄漏問題的排查[1]
例2:內(nèi)存居高不下
現(xiàn)象
內(nèi)存使用量(mem.rss)居高不下,且低峰期未下降,懷疑發(fā)生了內(nèi)存泄漏現(xiàn)象;
排查
剛開始懷疑時內(nèi)存泄漏,但是抓取 pprof heap 圖觀察后,未發(fā)現(xiàn)泄露問題,且內(nèi)存分配符合預期; 發(fā)現(xiàn)內(nèi)存使用雖然居高不下,但未呈上漲趨勢,因此修改關鍵字為“go 內(nèi)存占用居高不下”,發(fā)現(xiàn)有相同問題;
結(jié)論
問題來自于 GO 在將內(nèi)存歸還給操作系統(tǒng)時的內(nèi)存釋放策略,詳情見官方 issues[2],以下做簡單介紹。
GO 內(nèi)存釋放策略
(此節(jié)內(nèi)容整理自 壓測后go服務內(nèi)存暴漲[3])
不同策略的釋放機制
MADV_DONTNEED:內(nèi)核將會在合適的時機去釋放內(nèi)存,但進程的 RSS(常駐內(nèi)存)將會立即減少。如果再次申請內(nèi)存,內(nèi)核會重新分配一塊新的空間。 MADV_FREE:只能在 linux 內(nèi)核版本 4.5 以上才能使用,此操作理論上只是打了一個標記位,只有在內(nèi)核感覺到內(nèi)存壓力的時候才會將這些打標記的內(nèi)存回收掉,分配給其他進程使用。這個策略下進程的 RSS 不會立即減少。
不同策略的實際差別
理論上 MADV_FREE 效率要高一些,通過在頁表中做標記的方式,延遲內(nèi)存的分配和回收,可以提高內(nèi)存管理的效率,畢竟內(nèi)存的回收和分配都是會消耗系統(tǒng)性能的; 導致的 RSS 指標變化 MADV_DONTNEED 會導致進程 RSS 會有明顯的下降;MADV_FREE 會導致進程 RSS 平穩(wěn)在高峰,不會得到立即釋放;
不同 GO 版本的釋放策略
在 GO1.12 之前,默認均選擇的 MADV_DONTNEED 策略進行內(nèi)存回收; 在 GO1.12~GO1.15,官方默認選擇 MADV_FREE 策略進行內(nèi)存回收; 在 GO1.16 及之后,又改回了 MADV_DONTNEED 策略進行回收內(nèi)存。
在 GO1.12~GO1.15 且內(nèi)核版本 4.5 以上,mem.rss 指標已經(jīng)無法準確觀測服務內(nèi)存占用;
解決方法
不解決,對程序性能有利,但是會降低一些可觀測性; 以下任一方法可以解決,但會損失一定性能 把 export GODEBUG=madvdontneed=1 寫進服務 control.sh 腳本; 升級 GO 版本至 1.16 及以上;
參考資料
一起 goroutine 泄漏問題的排查: https://zhuanlan.zhihu.com/p/100740270
[2]issues: https://github.com/golang/go/issues/42330
[3]壓測后go服務內(nèi)存暴漲: http://soiiy.com/go/17114.html
推薦閱讀
