Go的棧空間管理

棧空間管理的基本邏輯

go語言經過goroutine提供了併發編程支持,goroutine是go運行庫的功能,而不是操做系統線程實現的,goroutine能夠被理解成一個用戶態的線程。golang

既然goroutine是由go運行庫管理的,那麼go運行庫也須要爲每一個goroutine建立並管理相應的棧空間,爲每一個goroutine分配的棧空間不能太大,goroutine開多時會浪費大量空間,也不能過小,會致使棧溢出。go語言選擇棧的棧空間管理的方式是,一開始給一個比較小的空間,隨着須要自動增加。當goroutine不須要那麼大的空間時,棧空間也要自動縮小。編程

分段棧 Segment Stacks

在go 1.3以前,go使用分段棧。併發

分段棧實現了一種不連續可是能夠持續增加的棧,開始時,棧只有一個段,當須要更多的棧空間時,會分配一個新的段,和上一個棧雙向連接。這樣,一個棧就是由多個雙向連接的段所組成的。當新分配的段使用完畢後,新段會被釋放掉。函數

分段棧實現了棧的按需收縮,在增長新分段時也不須要對原有分段中的數據進行拷貝,使得goroutine的使用代價很是低廉。性能

分段棧的好處是能夠按需增加,空間利用率比較高,然而分段棧在某些狀況下也存在必定的瑕疵。當一個段即將用盡,這時使用for循環執行一個比較耗空間的函數,會致使函數執行時goroutine進行段的分配,而執行完成返回時,進行段的銷燬,這樣就會致使在循環中出現屢次棧的擴容和收縮,形成很大的性能損失,這種狀況被稱做棧分裂(Stack Split)。操作系統

連續棧 Contiguous Stacks

go 1.3推出了連續棧,連續棧使用了另一種策略,再也不把棧分紅一段一段的,當棧空間不夠時,直接new一個2倍大的棧空間,並將原先棧空間中的數據拷貝到新的棧空間中,然後銷燬舊棧。這樣當出現棧空間觸及邊界時,不會產生棧分裂的狀況。線程

繼續假設當前棧空間即將用盡,而且須要在for循環中執行一個比較消耗空間的函數。當該函數執行時,棧空間發生了擴容,變成原先2倍大小,函數執行完成一次後,棧空間的使用量縮小回執行前的大小,可是棧空間的使用量並無小於棧大小的1/4,不會觸發棧收縮,因此在整個for循環執行過程當中,不會反覆觸發棧空間的收縮擴容。cdn

總結

相比於分段棧,連續棧避免了某些場景下棧空間的的頻繁伸縮。有一點須要注意的是,連續棧的收縮也是須要從新申請一段空間(原先的1/2大小),並進行棧拷貝操做的。blog

點擊關注知乎專欄Golang私房菜

相關文章
相關標籤/搜索