Go Runtime(三):Goroutine栈管理
本文源码基线为 Go 1.26.2、Linux/amd64。文中的源码链接固定到 go1.26.2 tag;其他版本和架构可能具有不同的汇编入口、常量或实现细节。 goroutine 能够以较低成本大量创建,一个重要原因是它不需要预留传统线程栈那样大的连续空间。Go 为普通 G 使用可增长的连续栈:空间不足时分配更大的栈、复制仍在使用的部分、修正指针,再从原调用点继续执行。 本文只讨论 goroutine 栈的运行时管理。对象为什么逃逸到堆由编译器决定,堆对象如何分配放在下一篇:内存分配器中。 三类栈不要混淆 一个 M 至少关联三种执行上下文: 上下文 作用 是否普通调度G 用户 G 的栈 执行用户 Go 函数,可增长和缩小 是 m.g0 的系统栈 调度、栈扩容、部分 runtime 内部路径 否 m.gsignal 的信号栈 Unix 信号处理 否 g0 和 gsignal 都是 g,但它们不是放进运行队列的普通 goroutine。newstack 必须在 g0 上运行,因为当前用户栈已经被判定为不足,不能继续依赖它完成内存分配和现场搬迁。 g中的栈字段 runtime.g 的栈相关字段是理解整条链路的入口: GOtype g struct { stack stack stackguard0 uintptr stackguard1 uintptr m *m sched gobuf syscallsp uintptr syscallpc uintptr // ... } type stack struct { lo uintptr hi uintptr } stack 描述实际区间 [lo, hi)。Go 栈在 amd64 上向低地址增长,SP 越来越接近 lo。stackguard0 是普通 Go 函数入口用于比较的保护值;正常情况下约为 stack.lo + stackGuard,也可以被设置成特殊值 stackPreempt,借用同一套入口检查触发协作式抢占。 ...