trace.Start() 默认写内存不落盘,需手动传入文件并调用 trace.Stop() 才能生成可解析的 trace.out;直接访问 /debug/trace 无效,须用 go tool trace trace.out 打开。

trace.Start() 启动后没看到数据?检查 runtime/trace 输出路径和浏览器打开方式
Go 的 trace.Start() 默认把追踪数据写进内存缓冲区,不自动落盘;你得手动调用 trace.Stop() 并把返回的 *os.File 写出到文件,否则浏览器打不开。常见错误是只调用了 trace.Start() 就去访问 http://localhost:6060/debug/trace —— 这个端点只对 net/http 服务有效,且需显式注册 net/http/pprof 路由。
- 正确做法:用
os.Create("trace.out")创建文件,传给trace.Start();程序结束前调用trace.Stop()关闭写入 - 快速验证:启动后执行
go tool trace trace.out,它会自动开浏览器;别直接用 Chrome 打开 .out 文件 - 注意兼容性:
go tool trace对 Go 版本敏感,1.21+ 生成的 trace 文件不能用 1.20 的工具解析
goroutine 分析卡在 “running” 状态?其实是被调度器阻塞,不是 CPU 占满
在 trace UI 的 Goroutines 视图里,看到大量 goroutine 显示为 running 但持续时间很长,容易误判为 CPU 密集型问题。实际多数情况是 goroutine 在等待系统调用(如 read、accept)、channel 阻塞或锁竞争,此时它处于可运行队列但未被调度器选中执行。
- 查根源:切换到
Network blocking或Synchronization blocking子视图,看是否集中在某个 fd 或 mutex 上 - 对比
Proc states:如果idle时间占比低,而runnable高,说明调度器过载或 GOMAXPROCS 设置不合理 - 避免踩坑:不要仅凭 goroutine 状态判断性能瓶颈,必须结合
Wall duration和Runnable delay柱状图看延迟分布
HTTP 请求延迟高,但 trace 里看不到 handler 入口?pprof 和 trace 的采样机制不一致
net/http/pprof 的 /debug/pprof/trace 是运行时采样,粒度粗(默认 100ms),而 runtime/trace 是事件驱动,记录所有 goroutine 创建、阻塞、GC 等精确时间点。如果你在 HTTP handler 里没手动打点,trace 不会自动标注请求生命周期——它只记录运行时行为,不理解业务语义。
- 补救方法:在 handler 开头加
trace.WithRegion(ctx, "http-handler"),结尾调用region.End() - 更轻量替代:用
trace.Log()记录关键节点,比如trace.Log(ctx, "db-query", "start"),这些会在 Events 视图中显示 - 性能影响:
trace.Log()开销约 50ns,比fmt.Println快一个数量级,但高频调用仍需节制
trace 文件太大打不开?不是数据多,是 GC 频率或 goroutine 泄漏导致事件爆炸
一个 500MB 的 trace.out 文件,往往不是因为跑了太久,而是某段逻辑反复创建 goroutine 且不退出(比如 for-select 里忘了 break),或者 GC 太频繁(GOGC=10 这类激进设置)。trace 工具加载时需将全部事件解压进内存,文件体积和 goroutine 数量呈近似线性关系。
立即学习“go语言免费学习笔记(深入)”;
- 先瘦身:用
go tool trace -pprof=heap trace.out看 goroutine 堆栈分布,找泄漏点 - 限长采集:启动时加
trace.Start(file, trace.WithBuffer(1 控制内存缓冲上限,避免 OOM - 真实场景建议:生产环境只在复现问题时开启 trace,单次采集控制在 30 秒内,配合
time.AfterFunc自动停止
trace 的价值不在“全量可视化”,而在定位那些无法用 pprof 抓住的调度延迟、阻塞点和跨 goroutine 时序异常——这些地方最容易被日志和指标掩盖。










