Appearance
pprof
- http://localhost:6060/debug/pprof/
- 安装 graphviz 支持 web 命令
bash
brew install graphvizCPU
目标:CPU 明显吃满的热点函数
bash
go run burn_cpu/burn_cpu.go采集 CPU profile(10 秒)
bash
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=10然后敲:
bash
(pprof) top
Showing nodes accounting for 8.71s, 99.43% of 8.76s total
Dropped 7 nodes (cum <= 0.04s)
flat flat% sum% cum cum%
8.30s 94.75% 94.75% 8.71s 99.43% main.burnCPU
0.41s 4.68% 99.43% 0.41s 4.68% runtime.asyncPreempt
0 0% 99.43% 0.05s 0.57% runtime.findRunnable
0 0% 99.43% 0.05s 0.57% runtime.mcall
0 0% 99.43% 0.05s 0.57% runtime.park_m
0 0% 99.43% 0.05s 0.57% runtime.schedule
(pprof)burnCPU 会稳稳地排第一!这是保证的,因为它 100% 吃你的 CPU。
看源码级别的热点行
bash
(pprof) list burnCPU
Total: 8.76s
ROUTINE ======================== main.burnCPU in /Users/luca/dev-test/gogc/burn_cpu/burn_cpu.go
8.30s 8.71s (flat, cum) 99.43% of Total
. . 10:func burnCPU() {
. . 11: for {
. . 12: // 这里故意做大循环,让 CPU 明显上升
. . 13: x := 0
8.28s 8.68s 14: for i := 0; i < 500_000_000; i++ {
20ms 30ms 15: x += i
. . 16: }
. . 17:
. . 18: // 防止被内联(没什么必要,但更保险)
. . 19: if x < 0 {
. . 20: panic("never happen")
(pprof)看 flame graph / 调用图
bash
web查看火焰图
bash
go tool pprof -http=:8011 http://localhost:6060/debug/pprof/profile?seconds=20内存泄漏
目标:让 heap profile 的 top 第一名永远是你写的函数 leakMem,占非常高的比例(几十 MB)
bash
go run leak_mem/leak_mem.go几秒后抓
bash
go tool pprof http://localhost:6060/debug/pprof/heappprof 里敲 top
bash
(pprof) top
Showing nodes accounting for 640.03MB, 99.53% of 643.03MB total
Dropped 23 nodes (cum <= 3.22MB)
flat flat% sum% cum cum%
640.03MB 99.53% 99.53% 640.03MB 99.53% main.leakMem
(pprof)看具体行
bash
(pprof) list leakMem
Total: 643.03MB
ROUTINE ======================== main.leakMem in /Users/luca/dev-test/gogc/leak_mem/leak_mem.go
640.03MB 640.03MB (flat, cum) 99.53% of Total
. . 12:func leakMem() {
. . 13: for {
640.03MB 640.03MB 14: b := make([]byte, 5*1024*1024) // 每次分 5MB
. . 15: global = append(global, b) // 不释放 → 永久持有
. . 16: time.Sleep(200 * time.Millisecond)
. . 17: }
. . 18:}
. . 19:
(pprof)查看火焰图
bash
go tool pprof -http=:8012 http://localhost:6060/debug/pprof/heapgoroutine 泄漏
目标:让 goroutine profile 里清晰出现几十条 goroutine,全部卡死在同一个点
bash
go run goroutine_leak/goroutine_leak.go抓 goroutine profile 并进入交互:
bash
go tool pprof http://localhost:6060/debug/pprof/goroutine按累积调用统计
bash
(pprof) top -cum
Showing nodes accounting for 1003, 99.90% of 1004 total
Dropped 30 nodes (cum <= 5)
flat flat% sum% cum cum%
1003 99.90% 99.90% 1003 99.90% runtime.gopark
0 0% 99.90% 1000 99.60% main.leakGoroutine
0 0% 99.90% 1000 99.60% runtime.chanrecv
0 0% 99.90% 1000 99.60% runtime.chanrecv1
(pprof)分析:
- flat:直接在这个函数里消耗的时间/事件数
- cum:包含它下面所有调用的总和
- runtime.gopark:表示 goroutine 被阻塞(park)
- main.leakGoroutine:你自己的函数,它创建了阻塞 goroutine
- runtime.chanrecv / chanrecv1:说明 goroutine 正在 <-ch 上等待
- 为什么 flat=0 但 cum=1000
结论:top 输出是正常的,cum 才是你要看的
- goroutine profile 并不是 CPU 时间,而是“事件快照”(采样 goroutine 堆栈)
- 你看到
flat=0是因为泄漏的 goroutine 并没有在消耗 CPU - cum=1000 才是重点:有 1000 个 goroutine 堆栈顶层是
main.leakGoroutine,全部被阻塞 - 这就是典型泄漏:goroutine 很多,但 flat=0 因为它们没在跑(被
<-ch卡住了)
看源码哪一行造成阻塞
bash
(pprof) list leakGoroutine
Total: 1004
ROUTINE ======================== main.leakGoroutine in /Users/luca/dev-test/gogc/goroutine_leak/goroutine_leak.go
0 1000 (flat, cum) 99.60% of Total
. . 9:func leakGoroutine(ch chan struct{}) {
. 1000 10: <-ch // 永远阻塞
. . 11:}
. . 12:
. . 13:func manyLeaks() {
. . 14: ch := make(chan struct{})
. . 15: for i := 0; i < 1000; i++ {
(pprof)top 找数量/热点,list 找具体行,web 看全局堆栈
查看火焰图
bash
go tool pprof -http=:8013 http://localhost:6060/debug/pprof/goroutine?debug=2