runtime.NumCPU() 返回操作系统报告的逻辑核数,非物理核且容器中无视 CPU 配额;NumGoroutine() 是瞬时快照,含系统 goroutine,仅适合低频趋势观察;ReadMemStats() 仅反映 Go 堆内存,非系统级内存;Timer/Ticker 在 Go 1.23 后支持自动回收,但仍需显式 Stop 防泄漏。

runtime.NumCPU() 返回的是逻辑核,不是物理核
它读的是操作系统报告的“可用处理器数”,在超线程开启的机器上会把每个超线程当做一个 CPU;在容器里更危险——runtime.NumCPU() 通常直接返回宿主机的逻辑核数,和当前容器被分配的 cpu.cfs_quota_us 完全无关。
如果你用它做并发度控制(比如启动 runtime.NumCPU() 个 worker),在 Kubernetes 里很可能跑满整个节点。真实受限场景下,得自己读 /sys/fs/cgroup/cpu/cpu.cfs_quota_us 和 /sys/fs/cgroup/cpu/cpu.cfs_period_us 算出实际配额,再向下取整。
- Linux 容器中建议 fallback 到解析 cgroup 文件,而非信任
runtime.NumCPU() - macOS / Windows 下该函数行为一致但无 cgroup,可作参考,不可用于资源硬限
- 不要把它当“我能用几个核”的权威答案,它只是“OS 告诉我的数字”
runtime.NumGoroutine() 是快照,不能当实时指标用
它返回的是调用瞬间的 goroutine 总数,包括 runtime 自己维护的系统 goroutine(比如 timerproc、sysmon、netpoll 相关的),业务代码根本无法区分哪些是自己的。
高频采集(比如每 10ms 调一次)不仅没意义,还会因原子计数器竞争带来可观开销;设绝对阈值告警(如 “>5000 就发警报”)也容易误报——GC 阶段或 netpoll 活跃时它天然飙升。
立即学习“go语言免费学习笔记(深入)”;
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 只适合做低频趋势观察(例如每 30 秒打点一次,画折线图)
- HTTP handler 里别调,尤其别嵌在日志里每请求都打一次
- 排查泄漏时,要结合
pprof/goroutine?debug=2看堆栈,而不是盯着这个数字猜
ReadMemStats 得到的是 Go 进程堆视图,不是系统内存使用率
runtime.ReadMemStats() 返回的 MemStats 结构体全是 Go runtime 自己管理的内存:堆分配、栈、mmap 映射页、GC 元数据……但它完全不感知 OS 缓存、其他进程、swap、甚至 Go 自己未归还给系统的内存页。
想算“本进程占系统内存百分之几”?直接用 MemStats.Alloc / totalRAM 是错的——Alloc 只是已用堆,totalRAM 却是物理总内存;更糟的是,MemStats.Sys 虽然接近进程 RSS,但它含未映射页,且不含共享库、VDSO 等非 heap 内存。
- 真要看系统级内存占用,Linux 下必须读
/proc/meminfo解析MemAvailable: - 防 OOM 更靠谱的做法是盯
MemStats.HeapInuse + MemStats.StackInuse的增长斜率,而不是百分比 - 别在循环里反复调
ReadMemStats,它会触发 stop-the-world 小停顿(虽短但累积起来可观)
time.Timer 和 time.Ticker 在 Go 1.23 后才真正支持 GC 回收
Go 1.23 之前,time.Timer 和 time.Ticker 创建后若忘记调 .Stop(),就会一直卡在 runtime 的定时器堆里,导致 goroutine 和内存泄漏——GC 根本扫不到它们。
1.23 引入了“孤儿定时器自动回收”机制:Timer 对象本身变轻量,底层 runtimeTimer 隐藏在其内存尾部,并带引用计数;只要上层变量被 GC,底层定时器也会被清理。但这不等于可以放飞自我:
-
time.After()仍不推荐在长生命周期 goroutine 中滥用,它本质是NewTimer().C,没地方调Stop - 显式创建的
Timer或Ticker,只要生命周期可能超过其用途,就必须手动Stop - 在 HTTP handler 或异步任务里启动 ticker,没配
defer ticker.Stop()是常见泄漏源

















