同时开启10万个goroutine最少占用多少内存?

我们来一步步拆解实际能达到的最小内存占用。

1. 每个 goroutine 的核心内存组成(最小情况)

部分大小(字节)说明
goroutine 结构体 (g)≈ 300–400 字节包含调度信息、指针、状态等(精确值随版本小幅波动)
初始栈(stack)2 KiB = 2048 字节当前绝大多数平台的最小初始栈(_StackMin)
其他运行时开销≈ 100–300 字节包含 defer 链表指针、panic 记录、gc bitmap 标记位等
单 goroutine 理论最低≈ 2.4–2.8 KiB最干净的空循环场景下实际能看到的下限

关键点:初始栈就是 2 KiB(不是 2KB 有时被误写成 2048 字节),这是 Go 1.4 以来长期保持的值(少数提案讨论过改成 4 KiB,但截至 2026 年初主流版本仍为 2 KiB)。

2. 10 万个这样的 goroutine 最少占用多少?

计算两种典型现实值:

场景每协程估算10 万协程总计备注
最理想情况(接近理论下限)2.4 KiB240–260 MiB极干净、无额外分配、无 hot/cold 分离、无 PGO、无动态栈调整
更现实的干净空转场景≈ 3–4 KiB300–420 MiB包含少量运行时元数据、GC 扫描位、栈 guard page 对齐等
带 fmt.Println 的版本5–10 KiB+500 MiB – 1 GiB+因为 println 涉及缓冲、反射、字符串常量等,会把栈撑大甚至 grow 一次

结论(最少占用):

不断输出 “hello world” 的 10 万个 goroutine,最小能做到 ≈ 240–280 MiB

(前提是使用最简代码、不带任何缓冲、用 print 而不是 fmt、关闭 race detector、纯 amd64/linux 环境)

3. 最简测试代码(尽量接近最小内存)

package main

import (
    "runtime"
    "time"
)

func main() {
    for i := 0; i < 100_000; i++ {
        go func() {
            for {
                println("hello world")   // 用内置 println 比 fmt 省内存
                // time.Sleep(time.Millisecond)   // 注释掉会更“空转”
            }
        }()
    }

    // 让主协程不退出
    print("created 100k goroutines, RSS ≈ ? \n")
    runtime.GC()
    time.Sleep(30 * time.Second)
}

编译运行后用以下方式观察(最接近“最小”值):

# RSS(常驻内存)通常最接近你关心的值
watch -n 1 "ps -o pid,rss,cmd -p $(pgrep -f your-binary) | tail -1"

# 或者用 pprof 查看更精确的 /memory/classes/heap/stacks:bytes
go tool pprof http://localhost:6060/debug/pprof/heap

4. 实际观测到的典型范围

  • 纯 for { println(“hello world”) }:通常 320–480 MiB
  • 如果把 println 去掉,只 for {}:可降到 240–320 MiB
  • 如果改成 for { time.Sleep(time.Second) }:更接近 250–300 MiB

总结一句话:

10 万个只是不停输出 “hello world” 的 goroutine,最少能压到大约 240–280 MiB 左右(理想干净场景下),现实干净代码一般落在 300–450 MiB 区间。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注