直接调用runtime.stack(buf, false)可轻量捕获当前goroutine调用栈,需预分配足够缓冲(如1mb)、检查返回长度n以防截断,并用buf[:n]转字符串;设false避免全量dump导致stw或oom。

怎么用 runtime.Stack 抓当前 goroutine 的调用栈
直接调用 runtime.Stack 是最轻量的方式,适合调试时快速看主 goroutine 或当前执行点的栈。它返回的是字节切片,需要自己转成字符串解析:
buf := make([]byte, 1024*1024)
n := runtime.Stack(buf, false) // false 表示只抓当前 goroutine
fmt.Printf("stack:\n%s", string(buf[:n]))
注意:false 参数容易被忽略,设成 true 会尝试 dump 所有 goroutine(可能卡住或 OOM);缓冲区大小要预估好,太小会截断,runtime.Stack 返回值 n 就是实际写入长度,务必用 buf[:n] 而非整个 buf。
怎么安全地枚举所有 goroutine 并获取状态
Go 没有公开 API 直接列出 goroutine 列表或状态(如 waiting / running),但可通过 debug.ReadGCStats 或 pprof 间接推断;真正能稳定用的只有 runtime.Stack 配合 true 参数 —— 它输出所有 goroutine 的栈快照,格式固定,可解析:
- 每段以
goroutine N [status]:开头,status是真实状态(如runnable、syscall、waiting) - 后续是该 goroutine 的调用栈,含函数名、文件行号
- 输出无锁、无竞态,但耗时随 goroutine 数量线性增长,生产环境慎频发调用
别指望靠正则“提取 goroutine ID”做监控——ID 不唯一(重启复用),且 Go 1.19+ 后已不保证单调递增。
pprof 的 /debug/pprof/goroutine?debug=2 能看到什么
这是最实用的线上观测方式,启动 HTTP server 后访问该 endpoint,返回的是带状态标记的完整 goroutine 栈列表:
import _ "net/http/pprof"
go http.ListenAndServe("localhost:6060", nil)
关键点:
-
?debug=1:只显示 goroutine 数量摘要(一行) -
?debug=2:显示每个 goroutine 的状态 + 完整栈(可 grep 筛选chan receive或select) - 输出格式稳定,比手撸
runtime.Stack更可靠,也自带采样控制
别在没开 pprof 的服务里硬塞 runtime.Stack(true) —— 一旦 goroutine 上万,一次调用就可能拖慢整个服务。
为什么不能用 runtime.NumGoroutine() 判断是否泄漏
runtime.NumGoroutine() 只返回当前存活数,完全无法区分“正常长期运行”和“卡死未退出”的 goroutine。常见误判场景:
- HTTP server 的 idle 连接协程(
net/http.serverHandler)一直挂着,数量随连接波动 - 使用
time.Ticker但没 stop,每次 tick 都新建 goroutine?不,它复用一个,但没 close 会导致资源滞留 - channel 发送端关闭后,接收端还在
range,goroutine 仍存在但已阻塞
真要查泄漏,得结合 ?debug=2 输出反复对比,重点看重复出现的栈帧 + 长时间不变的状态(比如卡在 chan send 且 sender 已 exit)。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











