Golang處理內(nèi)存溢出方式
背景
最近系統(tǒng)在壓測過程中發(fā)現(xiàn)主程序在并發(fā)增大后會出現(xiàn)主程序閃退現(xiàn)象,幾經(jīng)波折,認為有可能是內(nèi)存溢出引起的
正好對 Golang 里分析 dump 這塊還沒怎么涉及,借此契機研究一下。
前言
查看社區(qū)后,發(fā)現(xiàn)在Golang中,發(fā)生內(nèi)存溢出通常會導(dǎo)致程序直接崩潰退出,而無法像其他語言一樣生成core dump文件。
因此,Golang社區(qū)提供了一些工具可以幫助用戶進行內(nèi)存分析。
以下是兩個常用的工具:
1. pprof
pprof是Golang內(nèi)置的性能分析工具,在分析內(nèi)存使用方面非常有用。
通過設(shè)置一個標志位并在程序退出時打開pprof服務(wù)器,程序可以向pprof服務(wù)器報告一些性能數(shù)據(jù),例如內(nèi)存使用情況。
可以在代碼中加入以下代碼,啟動pprof服務(wù)器并在本地8080端口進行訪問:
import "net/http" import _ "net/http/pprof" func main() { go func() { http.ListenAndServe("localhost:8080", nil) }() // ... }
啟動pprof服務(wù)器之后,可以訪問http://localhost:8080/debug/pprof/heap進行堆內(nèi)存分析,獲取內(nèi)存占用的堆快照。
上圖中框出的這 4 個部分應(yīng)該是平時最常用的,從上往下分別是:
- 阻塞分析: 比如,goroutine 的 wait。
- 內(nèi)存分析: 比如,內(nèi)存泄漏、內(nèi)存消耗異常等情況。
- 互斥鎖分析: 比如,觀察代碼里用到的sync.RWMutex 和 sync.Mutex 的具體情況。
- CPU 分析: 比如,排查哪些代碼較多地占用了 CPU 資源。
以下是一段消耗內(nèi)存的代碼,用于走讀流程,如下:
func main() { go func() { http.ListenAndServe("0.0.0.0:8899", nil) }() str := "gejigejigejigejigejigejigejigjeijgiewjiasdiahdkuhakudsfhakdshgkjasdkjgbakjsdfioajewoiepqbibgijqbgoipbjwebkjfqjkw egqhwejbgfaijwebgjqhb" for i := 0; i < 999; i++ { str += str } fmt.Scanln() }
在 web 頁面點擊「heap」入口,我們可以看到實時內(nèi)存的使用情況。
再通過以下命令進入到命令交互模式看看效果:
go tool pprof http://localhost:8899/debug/pprof/heap
進去之后輸入「top」,就能很直觀的看到哪個方法占用了內(nèi)存。
這里的幾個列的含義簡單羅列下:
flat
:當(dāng)前函數(shù)所占用的容量。flat%
:當(dāng)前函數(shù)所占用的容量,在總分配容量的百分比。sum%
:是從調(diào)用的最外層到當(dāng)前方法累加使用的容量占總?cè)萘康陌俜直?/li>cum
:當(dāng)前函數(shù)以及子函數(shù)所占用的容量。cum%
:當(dāng)前函數(shù)以及子函數(shù)所占用的容量,在總分配容量的百分比。- 最后一列是函數(shù)的名字
可以再輸入獲得更詳細的信息:
list main.main
上述指令會羅列出每行代碼占用的容量;其他類似,如下是分析CPU的
2. Go Memstats
Go Memstats是一個Golang的標準庫工具,用于收集和顯示內(nèi)存信息。
可以在代碼中導(dǎo)入“runtime”包,并調(diào)用“runtime.ReadMemStats(&mem)”函數(shù)收集內(nèi)存信息。
然后可以打印內(nèi)存使用情況的詳情,例如總分配量和分配的堆塊數(shù)量等。
以下是示例代碼:
import ( "fmt" "runtime" ) func main() { var mem runtime.MemStats runtime.ReadMemStats(&mem) fmt.Printf("TotalAlloc (Heap) = %v MiB\n", mem.TotalAlloc/mb) fmt.Printf("Alloc = %v MiB\n", mem.Alloc/mb) fmt.Printf("Sys = %v MiB\n", mem.Sys/mb) fmt.Printf("NumGC = %v\n", mem.NumGC) }
這將輸出程序中已分配的總內(nèi)存,已使用的內(nèi)存量,系統(tǒng)內(nèi)存使用情況和垃圾回收次數(shù)等信息。
使用這些信息,可以分析內(nèi)存占用量的增長和優(yōu)化內(nèi)存使用。
3. 程序 crash 的時候自動創(chuàng)建 dump 文件
大多數(shù)時候,我們可能沒有條件實時分析程序運行情況。
比如問題在生產(chǎn)環(huán)境偶發(fā)出現(xiàn),且無法在測試環(huán)境重現(xiàn)。
這個時候我們可以配置當(dāng)程序 crash 的時候自動保存 dump 文件。
先輸入命令「ulimit -a」看下當(dāng)前是否開啟了core file。
這里的數(shù)字單位是 block,具體需要根據(jù)所在的操作系統(tǒng)一個 block 對應(yīng)的大小來設(shè)置。如果是0的話說明未開啟??梢酝ㄟ^:
ulimit -c 1024 或者 ulimit -c unlimited 來設(shè)置 dump 文件的最大 size。
如果想要永久有效,則:
echo "ulimit -c unlimited" >> ~/.profile
讓程序發(fā)生 crash 的時候會自動生成 dump 文件
## 臨時有效 export GOBACTRACE=crash ## 永久有效 echo "export GOTRACEBACK=crash " >> ~/.profile
這就意味著,讓程序發(fā)生 crash 的時候會自動生成 dump 文件。
有了 dump 文件,可以用 gdb 或者 delve 工具來分析了(官方更建議我們使用 delve,對 Golang 的支持更好)
總之啊,無論你使用哪種工具,分析Golang內(nèi)存溢出問題需要耐心和技巧。
建議不僅可以使用這些工具進行數(shù)據(jù)分析,同時還應(yīng)該閱讀關(guān)于垃圾回收機制和內(nèi)存管理的文檔,嘗試實現(xiàn)更好的編碼習(xí)慣,以減少內(nèi)存溢出的可能性。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
利用go-kit組件進行服務(wù)注冊與發(fā)現(xiàn)和健康檢查的操作
這篇文章主要介紹了利用go-kit組件進行服務(wù)注冊與發(fā)現(xiàn)和健康檢查的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04Golang實現(xiàn)http server提供壓縮文件下載功能
這篇文章主要介紹了Golang實現(xiàn)http server提供壓縮文件下載功能,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01xorm根據(jù)數(shù)據(jù)庫生成go model文件的操作
這篇文章主要介紹了xorm根據(jù)數(shù)據(jù)庫生成go model文件的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12golang中sync.Map并發(fā)創(chuàng)建、讀取問題實戰(zhàn)記錄
這篇文章主要給大家介紹了關(guān)于golang中sync.Map并發(fā)創(chuàng)建、讀取問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07