0. 简介

前面我们分别介绍了堆空间管理的内存分配器和垃圾收集,这里我们简单介绍一下Go中栈空间的管理。

1. 系统栈和Go栈

1.1 系统线程栈

pthread_create8192KB

对于栈上的内存,程序员无法直接操作,由系统统一管理,一般的函数参数、局部变量(C语言)会存储在栈上。

1.2 Go栈

runtime

Go语言使用用户态协程goroutine作为执行的上下文,其使用的默认栈大小比线程栈高的多,其栈空间和栈结构也在早期几个版本中发生过一些变化:

  • v1.0 ~ v1.1 — 最小栈内存空间为 4KB;
  • v1.2 — 将最小栈内存提升到了 8KB;
  • v1.3 — 使用连续栈替换之前版本的分段栈;
  • v1.4 — 将最小栈内存降低到了 2KB;

2. 栈操作

runtime.stack[lo, hi)

栈的结构虽然非常简单,但是想要理解 Goroutine 栈的实现原理,还是需要我们从编译期间和运行时两个阶段入手:

cmd/internal/obj/x86.stacksplitruntime.morestackruntime.morestack_noctxtruntime.malgruntime.stackallocruntime.morestack
//go:nosplit

2.1 栈初始化

stackpoolstackLarge

其初始化函数如下,从下也可以看出,Go栈的内存都是分配在堆上的:

2.2 栈分配

_FixedStack = 2048_NumStackOrders = 4_StackCacheSize = 32768
runtime.stackLarge

2.3 栈扩容

cmd/internal/obj/x86.stacksplitruntime.morestackruntime.newstack

在此期间可能触发抢占。

接下来就是分配新的栈内存和栈拷贝,这里就不详细描述了。

2.4 栈缩容

runtime.shrinkstack
2KB
1/4runtime.copystack