Go 中的分段栈和连续栈的区别?

# 分段栈

在 Go 1.3 版本之前 ,中的栈和栈使用的分段栈结构是分段栈,随着goroutine 调用的连续函数层级的深入或者局部变量需要的越来越多时,运行时会调用 runtime.morestack 和 runtime.newstack创建一个新的区别栈空间,这些栈空间是中的栈和栈不连续的,但是分段当前 goroutine 的多个栈空间会以双向链表的形式串联起来,运行时会通过指针找到连续的连续栈片段。

分段栈虽然能够按需为当前 goroutine 分配内存并且及时减少内存的区别占用,但是中的栈和栈它也存在一个比较大的问题:

如果当前 goroutine 的栈几乎充满,源码库那么任意的分段函数调用都会触发栈的扩容,当函数返回后又会触发栈的连续收缩,如果在一个循环中调用函数,区别栈的中的栈和栈分配和释放就会造成巨大的额外开销,这被称为热分裂问题(Hot split)。分段

为了解决这个问题,连续Go 在 1.2 版本的时候不得不将栈的初始化内存从 4KB 增大到了 8KB。后来把采用连续栈结构后,又把初始栈大小减小到了 2KB。

# 连续栈

连续栈可以解决分段栈中存在的两个问题,其核心原理就是每当程序的栈空间不足时,初始化一片比旧栈大两倍的新栈并将原栈中的所有值都迁移到新的栈中,新的云服务器局部变量或者函数调用就有了充足的内存空间。使用连续栈机制时,栈空间不足导致的扩容会经历以下几个步骤:

调用用 runtime.newstack 在内存空间中分配更大的栈内存空间; 使用 runtime.copystack 将旧栈中的所有内容复制到新的栈中; 将指向旧栈对应变量的指针重新指向新栈; 调用 runtime.stackfree销毁并回收旧栈的内存空间;

copystack会把旧栈里的所有内容拷贝到新栈里然后调整所有指向旧栈的变量的指针指向到新栈, 我们可以用下面这个程序验证下,栈扩容后同一个变量的内存地址会发生变化。

package main func main() {      var x [10]int     println(&x)     a(x)     println(&x) } //go:noinline func a(x [10]int) {      println(`func a`)     var y [100]int     b(y) } //go:noinline func b(x [100]int) {      println(`func b`)     var y [1000]int     c(y) } //go:noinline func c(x [1000]int) {      println(`func c`) } 

程序的输出可以看到在栈扩容前后,变量x的内存地址的变化:

0xc000030738 ... ... 0xc000081f38 

 是不是很简单呢?

本文转载自微信公众号「Go编程时光」,可以通过以下二维码关注。转载本文请联系Go编程时光公众号。

应用开发
上一篇:当投资者经过第二阶段的认真学习之后又充满了信心,认为自己可以在市场上叱咤风云地大干一场了。但没想到“看花容易绣花难”,由于对理论知识不会灵活运用.从而失去灵活应变的本能,就经常会出现小赢大亏的局面,结果往往仍以失败告终。这使投资者很是困惑和痛苦,不知该如何办,甚至开始怀疑这个市场是不是不适合自己。在这种情况下,有的人选择了放弃,但有的意志坚定者则决定做最后的尝试。
下一篇:3、不明先知,根据相关征兆预测可能发生的事件,以便提前做好准备,赶紧注册相关域名。;不差钱域名;buchaqian抢先注册,就是这种敏感类型。预言是最敏感的状态。其次,你应该有眼力。所谓眼力,就是善于从社会上时不时出现的各种热点事件中获取与事件相关的域名资源。眼力的前提是对域名领域的熟悉和丰富的知识。