Go Runtime(三):Goroutine栈管理
本文源码基线为 Go 1.26.2、Linux/amd64。文中的源码链接固定到 go1.26.2 tag;其他版本和架构可能具有不同的汇编入口、常量或实现细节。 goroutine 能够以较低成本大量创建,一个重要原因是它不需要预留传统线程栈那样大的连续空间。Go 为普通 G 使用可增长的连续栈:空间不足时分配更大的栈、复制仍在使用的部分、修正指针,再从原调用点继续执行。 本文只讨论 goroutine 栈的运行时管理。对象为什么逃逸到堆由编译器决定,堆对象如何分配放在下一篇:内存分配器中。 先观察:普通函数为什么调用morestack 先写一个具有明确栈帧、且不会被内联的函数,保存为 stackcheck.go: GOpackage main //go:noinline func frame(x int) int { var buf [256]byte buf[0] = byte(x) return int(buf[0]) } func main() { println(frame(7)) } 关闭优化和内联,查看编译器生成的汇编: BASHgo tool compile -S -N -l stackcheck.go 在 main.frame 开头可以看到 SP 与栈保护值的比较,以及失败后对 runtime.morestack_noctxt 的调用。一个普通函数为何需要在入口询问 runtime,又如何在栈被替换后重新执行?下面先建立三类栈和 G 的现场模型,再回到另一个实验观察整段活动栈的迁移。-N -l 只用于减少实验干扰,不代表生产构建一定生成相同指令。 三类栈不要混淆 一个 M 至少关联三种执行上下文: ...