runtime.stack 默认传 false 只获取当前 goroutine 栈,需传 true 才获取全部;注意缓冲区大小、避免高频调用、安全落盘(如 os.writefile),并区分适用场景——仅适用于 panic 抢救、死锁分析等特定调试。

runtime.Stack 为什么经常看不到 goroutine 全貌
因为默认调用 runtime.Stack 时传了 false,只 dump 当前 goroutine 的栈,不是全部。线上出问题时你真正想看的是“谁在阻塞”“谁占着锁”“哪些 goroutine 卡在 IO”,而不是当前调用者的几行调用链。
- 正确做法是传
true:runtime.Stack(buf, true)—— 这才获取所有 goroutine 的堆栈快照 -
buf必须预先分配足够空间,比如buf := make([]byte, 1024*1024);太小会截断,且函数返回实际写入长度,需用string(buf[:n])截取 - 频繁调用
runtime.Stack(true)有明显性能开销(遍历所有 goroutine + 格式化字符串),别放在 hot path 或定时器里狂打日志
怎么安全地把 stack 日志输出到文件或 HTTP 接口
直接 fmt.Printf 或 log.Print 容易炸内存或阻塞,尤其在高并发 panic 前夕。核心原则是:不格式化、不分配、尽快落盘。
- 用
os.WriteFile一次性写入原始字节(buf[:n]),避免 string 转换和 GC 压力 - 如果走 HTTP,用
http.ResponseWriter.Write(buf[:n])直接吐出,别包装成 JSON 或加额外字段——排查时要的是原生可读性 - 注意权限:写文件前确认目录存在且进程有写权限,否则静默失败;建议路径硬编码为
/tmp/goroutine-stacks-$(date +%s).txt类似格式,避免覆盖
线上环境用 runtime.Stack 需绕开的三个坑
本地跑通不等于线上可用。真实部署中常因环境差异导致 stack 获取失败或内容异常。
- CGO disabled 时某些 runtime 行为受限,但
runtime.Stack不受影响;真正的问题是:容器中/proc/self/maps不可读会导致部分符号无法解析(显示 ??:0),这不是Stack的锅,但你会误以为“没拿到栈” - 使用
go build -ldflags="-s -w"去除调试信息后,stack 输出只剩地址(如0x456789),无法对应源码行——线上 debug 用的二进制建议保留 DWARF(去掉-s),用 strip 单独处理发布包 - goroutine 处于系统调用中(如
read,epoll_wait)时,stack 可能停在runtime.gopark或runtime.netpollblock,这是正常态,不代表卡死;重点看是不是大量 goroutine 堆在同一个 channel send / recv / mutex.lock
替代方案:什么时候不该用 runtime.Stack
当你要查的是“为什么 CPU 爆了”“为什么内存涨不停”,runtime.Stack 提供的信息维度就不够。它只回答“此刻谁在跑”,不回答“他们在干什么”“干了多久”“分配了多少对象”。
立即学习“go语言免费学习笔记(深入)”;
- CPU 分析优先用
pprof.StartCPUProfile+net/http/pprof,比翻几千行 stack 更准 - 怀疑内存泄漏?
runtime.ReadMemStats和pprof.WriteHeapProfile比 stack 更直接 - 想实时观测 goroutine 数量变化?
debug.ReadGCStats和runtime.NumGoroutine()配合 Prometheus 指标更轻量可靠
真正需要 runtime.Stack 的时刻很窄:panic 后抢救现场、死锁复现时抓快照、或者 debug 一个疑似被 channel 卡住的长生命周期 goroutine。其他时候,先想清楚你要的答案到底在哪个层面。










