trace.Start必须配对trace.Stop,否则缓冲区填满后自动停止、goroutine泄漏、内存持续增长甚至OOM;需用os.Create创建可seek文件传入,且程序须运行超100ms并触发调度/GC事件。

Go 的 trace.Start 不适合线上长期开启,它会产生大量 I/O 和内存开销,仅适用于短时诊断。
trace.Start 为什么必须配 trace.Stop?
Go trace 是基于内存缓冲区的环形日志系统,trace.Start 启动后会持续写入二进制 trace 数据到内存(默认 64MB 缓冲区),但不会自动 flush 或释放。不调用 trace.Stop 会导致:
- 缓冲区填满后 trace 自动停止,但后续调用
trace.Start会 panic:trace: cannot start, already started - goroutine 泄漏:底层 goroutine 持续轮询 runtime 事件,无法回收
- 内存持续增长,尤其在高并发服务中可能触发 OOM
正确用法是成对使用,且建议用 defer trace.Stop() 确保退出:
func main() {
f, _ := os.Create("trace.out")
defer f.Close()
trace.Start(f)
defer trace.Stop() // 必须放在 Start 之后、逻辑之前
// ... 业务代码
}
trace.Start 写入文件失败的常见原因
trace 数据必须写入可写、支持 seek 的 io.Writer,常见失败场景包括:
立即学习“go语言免费学习笔记(深入)”;
- 传入
os.Stdout或log.Writer():它们不支持Seek,会 panic 报错:trace: writer must be seekable - 文件路径父目录不存在:需提前
os.MkdirAll创建 - 权限不足(如只读挂载点):错误信息为:
open trace.out: permission denied - 磁盘满或 inodes 耗尽:trace 写入时静默失败,但最终
trace.Stop()会返回 error
安全写法:
f, err := os.Create("trace.out")
if err != nil {
log.Fatal(err) // 不要忽略 err
}
defer f.Close()
if err := trace.Start(f); err != nil {
log.Fatal(err) // Start 也可能返回 error(比如 writer 不可 seek)
}
trace.Start 后看不到 Goroutine 调度事件?
默认 trace 只记录 GC、heap、processor、network poller 等基础事件,Goroutine 调度(如 go create、go start、goready)需要额外启用 runtime 调试标志:
- 启动程序时加
-gcflags="-l"无帮助,真正起作用的是GODEBUG=schedtrace=1000—— 但这只是打印文本调度日志,不写入 trace 文件 - trace 中调度事件由 runtime 内部控制,Go 1.20+ 默认已开启,但若 trace 文件过小(
- 确保 trace 运行时间足够长(至少 1–2 秒),短于 100ms 的 trace 很可能不包含完整调度周期
- 用
go tool trace trace.out打开后,按1键进入 Goroutine analysis 视图,才能看到 goroutine 生命周期图;直接看「View trace」里默认不展开调度细节
trace 数据本身不压缩,1 秒典型 HTTP 服务 trace 可达 20–50MB,分析前记得确认磁盘空间和 go tool trace 的内存上限。别把 trace.Start 当 profiling 工具用,它不是 pprof —— 它记录的是事件流,不是采样统计。



















