2.6 简述一下 Go 栈空间的扩容/缩容过程?¶
扩容流程¶
为啥会有栈空间扩容
由于当前的 Go 的栈结构使用的是连续栈,并且初始值才 2k 比较小,因此随着函数的调用层级加深,Go 的初始栈空间就可能不够用,不够用的话,就会触发栈空间的扩容。
栈空间扩容啥时会触发
编译器会为函数调用插入运行时检查runtime.morestack
,它会在几乎所有的函数调用之前检查当前goroutine
的栈内存是否充足,如果当前栈需要扩容,会调用runtime.newstack
创建新的栈。
而新的栈空间,是旧栈空间大小(通过保存在goroutine
中的stack
信息里记录的栈区内存边界计算出来的)的两倍,但最大栈空间大小不能超过
maxstacksize
,也就是 1G。
缩容流程¶
为啥会有栈空间缩容
在函数返回后,对应的栈空间会回收,如果调用栈比较深,那么随着函数一个一个返回,回收的栈空间会越来越多。假设在调用栈最深的时候,整体的栈空间扩容到了 100M,那么随着函数的返回,到某一个函数的时候,100M 的栈空间只有 1M 是实际占用的,内存利用率只有区区的 1% ,实在太浪费了。
栈空间缩容啥时会触发
因此在垃圾回收的时候,有必要检查一下栈空间里内存利用率,当利用率低于 25% 时,就要开始进行缩容,缩容成原来的栈空间的 50%,但同时也不能小于栈空间的原始值即最小值,2KB。
相同点¶
不管是扩容还是缩容,都是使用 runtime.copystack
函数来开辟新的栈空间,然后将旧栈的数据全部拷贝至新的栈空间,并调整原来指针的指向。