Go 內存泄漏排查兩例

我是一隻可愛的土撥鼠,專注於分享 Go 職場、招聘和求職,解 Gopher 之憂!歡迎關注我。

歡迎大家加入 Go 招聘交流羣,來這裏找志同道合的小夥伴!跟土撥鼠們一起交流學習。

例 1:Goroutine 泄漏

現象

NumGoroutine 指標持續上漲,且低峯期未下降,判斷出現了 Goroutine 泄漏現象。

排查

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

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

結論

http

下面以本次 case http 服務爲例,做簡單介紹:

當這兩種情況同時發生時,如果上游持有對本服務的連接不進行釋放,那麼服務端會一直維持這個連接的存在,不進行回收,進而導致協程泄漏;

client 上游客戶端可能爲 GO、Java 等,以下爲 GO 語言 http 客戶端的空閒連接超時設置;server 解決建議啓動 http server 儘量用後者,前者雖然簡單,但是服務不夠健壯;

thrift

server

Tips

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

Reference

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

例 2:內存居高不下

現象

內存使用量(mem.rss)居高不下,且低峯期未下降,懷疑發生了內存泄漏現象;

排查

  1. 剛開始懷疑時內存泄漏,但是抓取 pprof heap 圖觀察後,未發現泄露問題,且內存分配符合預期;

  2. 發現內存使用雖然居高不下,但未呈上漲趨勢,因此修改關鍵字爲 “go 內存佔用居高不下”,發現有相同問題;

結論

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

GO 內存釋放策略

(此節內容整理自 壓測後 go 服務內存暴漲 [3])

不同策略的釋放機制

不同策略的實際差別

不同 GO 版本的釋放策略

在 GO1.12~GO1.15 且內核版本 4.5 以上,mem.rss 指標已經無法準確觀測服務內存佔用;

解決方法

參考資料

[1]

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

[2]

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

[3]

壓測後 go 服務內存暴漲: http://soiiy.com/go/17114.html

Go 招聘

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/bGnQnN84opogyeMRwIPSHA