Skip to main content

Go 的 Stack 與 Heap

分配機制、生命週期、管理方式以及 Go 的逃逸分析來理解 Stack(棧)與 Heap(堆)的差異。


1. 核心概念與用途

Stack(棧 / 堆棧)

  • 用途:函數執行,現代電腦執行模型(基於「堆棧」)的基礎。
  • 內容:函數的局部變數、參數、返回值與調用時的上下文資訊。
  • 特性:分配與回收極快,隨函數調用與返回自動進行(LIFO,後進先出)。

Heap(堆)

  • 用途:存放需跨越函數邊界、或體積較大的資料。
  • 內容:動態分配的物件。
  • 特性:分配彈性大,管理成本較高;在 Go 中由垃圾回收器 (GC) 回收。

2. 逃逸分析 (Escape Analysis)

Go 與 C/C++ 的重要差異:在 C 中回傳局部變數的位址會造成懸空指標,在 Go 中則可合法使用,靠的是逃逸分析

  • 機制:編譯器分析變數作用域;若函數內的局部變數在函數返回後仍被引用(例如回傳其位址),則判定該變數「逃逸」出棧。
  • 結果:逃逸的變數會被自動改放到 Heap,以保證函數返回後仍可使用。
  • 範例
func sum(a, b int) *int {
s := a + b
return &s // s 經逃逸分析後會分配到 Heap
}

3. 分配原語:newmake

函數功能回傳存放位置
new(T)分配並置零 (zeroed)指向零值的指標 *TStack 或 Heap,依逃逸分析
make(T, args)建立 slicemapchannel已初始化的值 T(非指標)依逃逸分析
  • 為何需要 make:slice、map、channel 底層為引用型別,必須先初始化內部結構(指標、長度、容量等)才能使用,make 負責這份初始化。

4. Goroutine 的 Stack 管理

  • 動態成長:Goroutine 的 Stack 初始很小(數 KB),隨需要自動擴展。
  • 與 Heap 的關係:Stack 空間不足時,執行時會在 Heap 上分配新區塊來擴展 Stack。

總結比較

項目Stack(棧)Heap(堆)
主要用途函數調用、局部變數、參數傳遞動態分配、跨函數共享、大物件
生命週期隨函數調用建立,隨返回銷毀由 GC 管理,無引用時回收
分配速度極快(編譯期/指令)較慢(執行期尋找空間)
由誰決定變數未逃逸變數發生逃逸
常見寫法var i int 等一般宣告newmake& 取址(是否上堆仍看逃逸)
Goroutine初始小、可動態成長Stack 成長時會使用 Heap 空間

一句話:Go 不需像 C 一樣手動管理 Heap(malloc/free),編譯器透過逃逸分析自動決定變數放在 Stack(求效能)或 Heap(保生存期)。