runtime.NumGoroutine() 返回当前存活的 goroutine 总数,包括运行、就绪、阻塞及刚启动未调度的协程,涵盖用户与 runtime 内部协程,是瞬时无锁快照,不可直接反映泄漏或并发请求数。

runtime.NumGoroutine() 返回什么值
runtime.NumGoroutine() 返回当前程序中**存活的 goroutine 总数**,包括正在运行、就绪、阻塞(如在 channel 操作、syscall、time.Sleep)甚至刚启动还没调度的 goroutine。它不区分“用户创建”还是“runtime 内部使用”,比如 net/http 的监听协程、gc 相关的辅助 goroutine 都算在内。
常见错误现象:监控告警突然飙高,但业务逻辑没明显变化——很可能是忘了 http.Server 启动后自带的监听 goroutine、或 pprof 服务常驻的 handler 协程也被计入了。
- 这个值是瞬时快照,无锁读取,开销极低,可高频调用(比如每秒一次)
- 它不等于“并发请求数”,一个 HTTP handler 可能 spawn 多个子 goroutine,也可能复用同一个
- 无法区分 goroutine 生命周期阶段,比如已退出但尚未被 runtime 清理的 goroutine 仍会计入(极少,通常可忽略)
怎么安全地在生产环境采集 Goroutine 数量
直接裸调 runtime.NumGoroutine() 没问题,但采集方式决定是否可靠。关键不是“能不能读”,而是“读出来有没有意义”。
使用场景:Prometheus 指标暴露、Zabbix 主动拉取、或日志中周期性打点做趋势分析。
立即学习“go语言免费学习笔记(深入)”;
- 避免在 panic 或 defer 中调用——虽然函数本身不会 panic,但若此时栈已损坏,数值可能失真
- 不要在
init()里采集,那时 runtime 还没完全初始化,返回值可能为 0 或异常小 - 推荐封装成 Prometheus
Gauge,用promhttp暴露:go g := promauto.NewGauge(prometheus.GaugeOpts{ Name: "go_goroutines", Help: "Number of goroutines running", }) // 定期更新 go func() { for range time.Tick(5 * time.Second) { g.Set(float64(runtime.NumGoroutine())) } }()
为什么 NumGoroutine 突增却没发现泄漏
因为 goroutine 泄漏的本质是“该结束的没结束”,而 runtime.NumGoroutine() 只给总数,不提供堆栈或归属信息。你看到数字涨了,但不知道是谁、在哪、为什么卡住。
典型误判:把临时 burst 当成泄漏。比如并发处理 1000 个文件,每个启一个 goroutine,全跑完才回收——高峰期数值冲到 1000+,但几分钟后回落,这不是泄漏。
- 真正泄漏的标志是:数值持续缓慢上涨,且与请求量/定时任务频率不成比例
- 必须配合
debug.ReadGCStats或 pprof:访问/debug/pprof/goroutine?debug=2查看完整堆栈 - 注意
goroutinepprof 默认只抓正在运行/阻塞的;加?debug=1才包含空闲状态(更全,但更重) - 常见坑:忘记
select { case ,导致超时后 goroutine 还在等 channel
NumGoroutine 和 GOMAXPROCS、GOGC 有关系吗
没有直接关系。runtime.NumGoroutine() 是运行时状态计数器,和调度策略参数无关。
但间接影响明显:比如把 GOMAXPROCS 设得过小(如 1),大量 goroutine 会排队等待 M,表现为高数量 + 低 CPU 利用率;而 GOGC 调得太松,GC 延迟升高,可能让某些依赖 GC 清理资源的 goroutine 滞留更久(如带 finalizer 的对象)。
-
GOMAXPROCS影响的是“最多几个 OS 线程同时执行 Go 代码”,不控制 goroutine 创建数量 -
GOGC控制堆增长阈值,不影响 goroutine 生命周期,除非你的 goroutine 显式依赖runtime.GC()或 finalizer - 真正该盯的是
runtime.ReadMemStats里的NumGC和PauseNs,它们和 goroutine 行为共同反映系统健康度
数值本身很简单,难的是判断它背后是不是藏着 channel 死锁、context 忘传、或者 select 漏写 default 分支——这些地方不报错,但会让 goroutine 沉底。










