使用多年的 go pprof 檢查記憶體洩漏的方法居然是錯的?!
最近在做一個 Redis 的 Proxy 的專案,其中利用 Redis 6.0 新加的 tracking 功能實現客戶端快取的功能,可以為某些特定的 redis 使用場景提高吞吐和延遲。
當然,cache 的實現也是有代價的。首先,cache 的大小不能無限制的大,否則總有一點會把記憶體撐爆的;其次,cache 的淘汰演算法有多種方式,LRU、LFU 等等,具體可以參考 Cache replacement policies,不同的場景下各種淘汰演算法的效果是不一樣的;第三,對於大併發情況實現 cache 是有代價的,因為併發情況下對 cache 的訪問需要加鎖,而加鎖就意味著有效能的損失。
我在實現這個 cache 的過程中稍微偷了一下懶, 想盡量的減少鎖的 scope,結果導致記憶體洩漏的問題。本來 cache 佔用的最大記憶體我設定為 10GB, 結果過了個週末發現程式已經佔用了 80GB 的記憶體了。
當然本文不是要介紹這個專案的記憶體洩漏原因,而是介紹一下 Go pprof 工具查詢記憶體洩漏的一個不太常用的方法。
檢查 Go 程式記憶體的使用情況最常用的就是 Go 標準庫自帶的 pprof 庫了,可以通過 http 暴露出這個 profile, 然後通過go tool pprof
或者pprof
工具命令列/web
方式檢視。
比如下面的命令, 可以獲取伺服器http://ip:port
的堆資訊,並且在本機 9090 埠啟動一個伺服器展示堆的資訊。
go tool pprof -http :9090 http://ip:port/debug/pprof/heap
在堆資訊中你可以檢視分配的堆的大小和物件數量,或者當前沒有釋放的佔用的堆的大小和物件數量。
正常情況下使用這個方式就可以比較直觀的看到哪一段程式碼分配的記憶體比較多,然後確定那裡容易產生記憶體洩漏。
但是, 分配堆記憶體比較多的地方並不一定產生記憶體洩漏,只能說明這個地方"曾經/正在"分配的堆記憶體比較大,或者分配的堆記憶體比較頻繁俄安,這些分配的記憶體可能在之後就回收掉了。
像 Java 的一些 profiler 工具一樣, pprof 也可以比較兩個時間點的分配的記憶體的差值,通過比較差值,就容易看到哪些地方產生的記憶體"殘留"的比較多,沒有被記憶體釋放,極有可能是記憶體洩漏的點。
你可以通過下面的方式產生兩個時間點的堆的 profile,之後使用 pprof 工具進行分析。
- 首先確保你已經配置了 pprof 的 http 路徑, 可以訪問
http://ip:port/debug/pprof/
檢視 (如果你沒有修改預設的 pprof 路徑) - 匯出時間點 1 的堆的
profile: curl -s http://127.0.0.1:8080/debug/pprof/heap > base.heap
, 我們把它作為基準點 - 喝杯茶,等待一段時間後匯出時間點 2 的堆的
profile: curl -s http://127.0.0.1:8080/debug/pprof/heap > current.heap
- 現在你就可以比較這兩個時間點的堆的差異了:
go tool pprof --base base.heap current.heap
操作和正常的go tool pprof
操作一樣, 比如使用 top 檢視使用堆記憶體最多的幾處地方的記憶體增刪情況:
使用web
命令會生成一個 SVG 檔案,可能你需要使用瀏覽器開啟它。
或者你直接使用命令開啟 web 介面: go tool pprof --http :9090 --base base.heap current.heap
。
原文地址:https://colobu.com/2019/08/20/use-pprof-to-compare-go-memory-usage/
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- 如何檢查Javascript中的記憶體洩漏JavaScript記憶體
- Pprof定位Go程式記憶體洩露Go記憶體洩露
- vue使用中的記憶體洩漏Vue記憶體
- 記憶體洩漏的原因記憶體
- Handler的使用、記憶體洩漏和解決記憶體
- iOS檢測記憶體洩漏iOS記憶體
- 定位並修復 Go 中的記憶體洩漏Go記憶體
- 記憶體洩漏除錯工具記憶體除錯
- Node.js 應用的記憶體洩漏問題的檢測方法Node.js記憶體
- WebView引起的記憶體洩漏WebView記憶體
- 【譯】JavaScript的記憶體管理和 4 種處理記憶體洩漏的方法JavaScript記憶體
- 記憶體的分配與釋放,記憶體洩漏記憶體
- Android中常見的記憶體洩漏Android記憶體
- .NET 記憶體洩漏的爭議記憶體
- Swift的ARC和記憶體洩漏Swift記憶體
- Android中的記憶體洩漏模式Android記憶體模式
- [譯] Swift 中的記憶體洩漏Swift記憶體
- 使用VLD進行記憶體洩漏檢測(release + debug)記憶體
- 分析記憶體洩漏和goroutine洩漏記憶體Go
- 前端面試查漏補缺--(十三) 記憶體洩漏前端面試記憶體
- Go 記憶體洩漏?不是那麼簡單!Go記憶體
- jvm 記憶體洩漏JVM記憶體
- Android 記憶體洩漏Android記憶體
- Java記憶體洩漏Java記憶體
- js記憶體洩漏JS記憶體
- Android記憶體洩漏Android記憶體
- 造成記憶體洩漏的操作有哪些?記憶體
- 如何避免JavaScript中的記憶體洩漏?JavaScript記憶體
- Flutter 上的記憶體洩漏監控Flutter記憶體
- 如何在 Linux 下檢測記憶體洩漏Linux記憶體
- java記憶體溢位和記憶體洩漏的區別Java記憶體溢位
- C/C++應用程式記憶體洩漏檢查統計方案C++記憶體
- 記一次使用windbg排查記憶體洩漏的過程記憶體
- 關於記一次 Go 服務記憶體洩漏問題調查Go記憶體
- 造成記憶體洩漏的異常處理記憶體
- 一次 Java 記憶體洩漏的排查Java記憶體
- Js中常見的記憶體洩漏場景JS記憶體
- 關於PHP記憶體洩漏的問題PHP記憶體