pprof

介紹

一般而言,性能分析可以從三個層次來考慮:應用層、系統層、代碼層。

應用層主要是梳理業務方的使用方式,讓他們更合理地使用,在滿足使用方需求的前提下,減少無意義的調用;系統層關注服務的架構,例如增加一層緩存;代碼層則關心函數的執行效率,例如使用效率更高的開方算法等。

做任何事,都要講究方法。在很多情況下,迅速把事情最關鍵的部分完成,就能拿到絕大部分的收益了。其他的一些邊邊角角,可以慢慢地縫合。一上來就想完成 100%,往往會陷入付出了巨大的努力,卻收穫寥寥的境地。

性能優化這件事也一樣,識別出性能瓶頸,會讓我們付出最小的努力,而得到最大的回報。

Go 語言裏,pprof 就是這樣一個工具,幫助我們快速找到性能瓶頸,進而進行有針對性地優化。

pprof 用來監控、分析性能的
可以查看
allocs: A sampling of all past memory allocations
block: Stack traces that led to blocking on synchronization primitives
cmdline: The command line invocation of the current program
goroutine: Stack traces of all current goroutines
heap: A sampling of memory allocations of live objects. You can specify the gc GET parameter to run GC before taking the heap sample.
mutex: Stack traces of holders of contended mutexes
profile: CPU profile. You can specify the duration in the seconds GET parameter. After you get the profile file, use the go tool pprof command to investigate the profile.
threadcreate: Stack traces that led to the creation of new OS threads
trace: A trace of execution of the current program. You can specify the duration in the seconds GET parameter. After you get the trace file, use the go tool trace command to investigate the trace.

使用

	import (
	"fmt"
	"net/http"
	_ "net/http/pprof"
)
func main() {
	//defer profile.Start().Stop()
 
	go func() {
		if err := http.ListenAndServe("0.0.0.0:8888", nil); err != nil {
			fmt.Println("0.0.0.0:8888", err.Error())
		}
	}()

}

分析

test

go test -bench=. -cpuprofile=cpu.prof

web 頁面

http://localhost:8888/debug/pprof/

在這裏插入圖片描述
查看當前程序運行時情況

goroutine profile: total 32023:32023是goroutine的總數量,

debug=2 可以看 goroutine 信息

交互

go tool pprof
在這裏插入圖片描述

list *** :查看某個函數
top 查看前幾個

flat: 本函數佔用的內存量。
flat%: 本函數內存佔使用中內存總量的百分比。
sum%: 前面每一行flat百分比的和,比如第2行雖然的100% 是 100% + 0%。
cum: 是累計量,加入main函數調用了函數f,函數f佔用的內存量,也會記進來。
cum%: 是累計量佔總量的百分比。

tree
web
traces : 調用棧信息

火焰圖

brew install graphviz 

go tool pprof -http=":8081" /var/folders/cm/k_vl2cns1t70xzn4jm3r81sm0000gn/T/profile565242515/cpu.pprof

內存泄漏

內存泄露指的是程序運行過程中已不再使用的內存,沒有被釋放掉,
導致這些內存無法被使用,直到程序結束這些內存才被釋放的問題。

Go雖然有GC來回收不再使用的堆內存,減輕了開發人員對內存的管理負擔,
但這並不意味着Go程序不再有內存泄露問題。
在Go程序中,如果沒有Go語言的編程思維,也不遵守良好的編程實踐,就可能埋下隱患,造成內存泄露問題。

goroutine泄漏

go啓動之後,沒有符合預期退出
goroutine佔用的內存就無法釋放,沒有辦法回收,造成內存泄漏
1、goroutine 本身的棧佔用的空間
2、goroutine 逃逸到堆佔用的內存,

如果內存隨着goroutine 增長,而不隨着下降,這時候可能就出現內存泄漏的問題了
可能是channel 阻塞,無法繼續執行,導致關聯的內存都無法釋放

channel 只讀不寫阻塞、寫滿阻塞、讀不出數據阻塞等

問題排查

先分析堆棧信息,看是否內存泄漏
heap可以幫助發現內存問題,

1、go tool pprof http://localhost:8888/debug/pprof/heap // 獲取2個profile
2、go tool pprof -base *.pb.gz * .pb.gz //對比兩個文件
3、 使用top等命令查看
4、traces 可以看出goroutine 多的地方
雖然可以找到內存使用最多的地方,但不一定是內存泄漏的地方
還需要分析具體的原因

goroutine 泄漏,heap無法發現

怎麼確定是否逃逸

第一,通過編譯器命令,就可以看到詳細的逃逸分析過程。而指令集 -gcflags 用於將標識參數傳遞給 Go 編譯器,涉及如下:

-m 會打印出逃逸分析的優化策略,實際上最多總共可以用 4 個 -m,但是信息量較大,一般用 1 個就可以了

-l 會禁用函數內聯,在這裏禁用掉 inline 能更好的觀察逃逸情況,減少干擾

go build -gcflags '-m -l' main.go

二,通過反編譯命令查看

go tool compile -S main.go
注:可以通過 go tool compile -help 查看所有允許傳遞給編譯器的標識參數

參考

https://wiki.jikexueyuan.com/project/go-command-tutorial/0.12.html
https://blog.csdn.net/skh2015java/article/details/102748222
https://www.cnblogs.com/qcrao-2018/p/11832732.html
https://cizixs.com/2017/09/11/profiling-golang-program/
https://www.jianshu.com/p/81b6c0df66d1
https://blog.51cto.com/onebig/2431869?source=dra
https://www.jianshu.com/p/ba9f07a346d5
https://blog.csdn.net/sunxianghuang/article/details/93869683
https://blog.wolfogre.com/posts/go-ppof-practice/
https://mp.weixin.qq.com/s/I9xSMxy32cALSNQAN8wlnQ
http://holys.im/2016/07/01/monitor-golang-gc/
https://segmentfault.com/a/1190000016412013
https://eddycjy.com/posts/go/talk/2019-05-20-stack-heap/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章