正文
一、Goroutine Profiling:
1. 源代码:
package main import ( "net/http" _ "net/http/pprof" "runtime" "sync" ) func init() { runtime.SetMutexProfileFraction(1) runtime.SetBlockProfileRate(1) } func main() { var m sync.Mutex var datas = make(map[int]struct{}) for i := 0; i < 999; i++ { go func(i int) { m.Lock() defer m.Unlock() datas[i] = struct{}{} }(i) } _ = http.ListenAndServe("0.0.0.0:2022", nil) }
2. 协程(goroutine)分析
项目更目录下执行 go tool pprof http://127.0.0.1:2022/debug/pprof/goroutine
,结束之后会默认进入 PProf 的命令行交互模式,接着执行 traces
,如下图,
- 在查看 goroutine 时可以使用 traces 命令,这个命令会打印出对应的所有调用栈,以及指标信息。通过这个命令我们可以很方便地查看整个调用链路有什么,分别在哪里使用了多少个 goroutine,并且通过分析可以知道谁才是真正的调用方。
- 调用栈的展示顺序是自下而上的,也就是说,
runtime.main
方法调用了main.main
方法,而main.main
方法又调用了 net/http.ListenAndServe 方法,这里对应的就是我们使用的示例代码了,非常方便。
- 每个调用栈信息都用
-------
分隔,函数方法前的是指标数据。例如,Goroutine Profiling 展示的是该方法占用的 goroutine 的数量,而 Heap Profiling 展示的是占用的内存大小。
3. 锁(mutex)分析
项目更目录下执行 go tool pprof http://127.0.0.1:2022/debug/pprof/mutex
,结束之后会默认进入 PProf 的命令行交互模式,接着执行 top
,如下图,
接着执行 list main
(list 命令,查看指定函数的代码情况(包含特定的指标小心,如耗时)。若函数名不明确,则默认会对函数名进行模糊匹配),如下图
从输出的分析中可以比较准确地看到引起互斥锁的函数,以及锁开销的位置(在本例中是第 23 行)。
4. 阻塞(block)分析
项目更目录下执行 go tool pprof http://127.0.0.1:2022/debug/pprof/block
,结束之后会默认进入 PProf 的命令行交互模式,接着依次执行 top
和 list main
,如下图,
从 list main
命令的输出结果来看,程序在第 22 行阻塞。