Go 面試題 012:Go 中的分段棧和連續(xù)棧的區(qū)別?
大家好,我是明哥。
歡迎大家再次來到??『Go 語言面試題庫』?這個專欄
本篇問題:Go 中的分段棧和連續(xù)棧的區(qū)別?
# 分段棧
在 Go 1.3 版本之前 ,使用的棧結(jié)構(gòu)是分段棧,隨著goroutine 調(diào)用的函數(shù)層級的深入或者局部變量需要的越來越多時,運行時會調(diào)用 runtime.morestack 和 runtime.newstack創(chuàng)建一個新的棧空間,這些棧空間是不連續(xù)的,但是當前 goroutine 的多個棧空間會以雙向鏈表的形式串聯(lián)起來,運行時會通過指針找到連續(xù)的棧片段。
分段棧雖然能夠按需為當前 goroutine 分配內(nèi)存并且及時減少內(nèi)存的占用,但是它也存在一個比較大的問題:
如果當前 goroutine 的棧幾乎充滿,那么任意的函數(shù)調(diào)用都會觸發(fā)棧的擴容,當函數(shù)返回后又會觸發(fā)棧的收縮,如果在一個循環(huán)中調(diào)用函數(shù),棧的分配和釋放就會造成巨大的額外開銷,這被稱為熱分裂問題(Hot split)。
為了解決這個問題,Go 在 1.2 版本的時候不得不將棧的初始化內(nèi)存從 4KB 增大到了 8KB。后來把采用連續(xù)棧結(jié)構(gòu)后,又把初始棧大小減小到了 2KB。
# 連續(xù)棧
連續(xù)??梢越鉀Q分段棧中存在的兩個問題,其核心原理就是每當程序的??臻g不足時,初始化一片比舊棧大兩倍的新棧并將原棧中的所有值都遷移到新的棧中,新的局部變量或者函數(shù)調(diào)用就有了充足的內(nèi)存空間。使用連續(xù)棧機制時,棧空間不足導致的擴容會經(jīng)歷以下幾個步驟:
調(diào)用用 runtime.newstack 在內(nèi)存空間中分配更大的棧內(nèi)存空間;
使用 runtime.copystack 將舊棧中的所有內(nèi)容復制到新的棧中;
將指向舊棧對應(yīng)變量的指針重新指向新棧;
調(diào)用 runtime.stackfree銷毀并回收舊棧的內(nèi)存空間;
copystack會把舊棧里的所有內(nèi)容拷貝到新棧里然后調(diào)整所有指向舊棧的變量的指針指向到新棧, 我們可以用下面這個程序驗證下,棧擴容后同一個變量的內(nèi)存地址會發(fā)生變化。
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的內(nèi)存地址的變化:
0xc000030738
...
...
0xc000081f38
是不是很簡單呢?
本系列的所有文章,我都開放到 Github 上:https://github.com/iswbm/golang-interview
這個號沒有留言功能呢?,如果文章有寫得不對的地方,可以去那里提交 issue 幫我指正。順便可以幫我點個小 ??,在那里我對題庫進行了分類整理,方便索引查找。
加油噢,我們下篇見!
? ?

???
