go區域性變數的儲存空間是堆還是棧?

傑克曼發表於2019-02-16

go區域性變數的儲存空間是堆還是棧?

編譯器會自動選擇在棧上還是在堆上分配區域性變數的儲存空間,但可能令人驚訝的是,這個選擇並不是由用var還是new宣告變數的方式決定的。

var global *int

func f() {
    var x int
    x = 1
    global = &x
}

func g() {
    y := new(int)
    *y = 1
}

f函式裡的x變數必須在堆上分配,因為它在函式退出後依然可以通過包一級的global變數找到,雖然它是在函式內部定義的;用Go語言的術語說,這個x區域性變數從函式f中逃逸了。相反,當g函式返回時,變數y將是不可達的,也就是說可以馬上被回收的。因此,y並沒有從函式g中逃逸,編譯器可以選擇在棧上分配*y的儲存空間(譯註:也可以選擇在堆上分配,然後由Go語言的GC回收這個變數的記憶體空間),雖然這裡用的是new方式。其實在任何時候,你並不需為了編寫正確的程式碼而要考慮變數的逃逸行為,要記住的是,逃逸的變數需要額外分配記憶體,同時對效能的優化可能會產生細微的影響。

Go語言的自動垃圾收集器對編寫正確的程式碼是一個巨大的幫助,但也並不是說你完全不用考慮記憶體了。你雖然不需要顯式地分配和釋放記憶體,但是要編寫高效的程式你依然需要了解變數的生命週期。例如,如果將指向短生命週期物件的指標儲存到具有長生命週期的物件中,特別是儲存到全域性變數時,會阻止對短生命週期物件的垃圾回收(從而可能影響程式的效能)。

相關文章