GO 語言變量的生命周期
生命周期是指程序執行過程中變量存在的時間段。
包變量(全局變量)和局部變量兩種變量的生命周期。
① 包變量一直常駐在內存到程序的結束,然后被系統垃圾回收器回收。也就是說包變量的生命周期是整個程序的執行時間。
② 局部變量,例如一個函數中定義的變量。它有一個動態的生命周期:每次執行生命語句時創建一個新的實體,變量一直生存到它變得不可訪問(例如沒有外部指針指向它,函數退出我們沒有路徑能訪問到這個變量),這時它占用的存儲空間就會被回收。
var p *int //全局指針變量
func f(){
var i int
i = 1
p = &x //全局指針變量指向局部變量i
}
func f(){
p := new(int) //局部指針變量,使用new申請的空間
*p = 1
}
i是通過var申請的局部變量,但是由於有外部指針指向訪問,我們有路徑可找到這個空間(變量能夠逃逸出函數),所以局部變量i是申請在堆空間上。而第二個程序中p指針變量雖然是使用new申請的空間,但是由於退出函數就沒有路徑可尋找到它(變量無法逃出函數),所以局部變量p是申請在棧空間上的。
f函數里的x變量必須在堆上分配,因為它在函數退出后依然可以通過包一級的global變量找到,雖然它是在函數內部定義的;用Go語言的術語說,這個x局部變量從函數f中逃逸了。相反,當g函數返回時,變量*y
將是不可達的,也就是說可以馬上被回收的。因此,*y
並沒有從函數g中逃逸,編譯器可以選擇在棧上分配*y
的存儲空間(譯注:也可以選擇在堆上分配,然后由Go語言的GC回收這個變量的內存空間),雖然這里用的是new方式。其實在任何時候,你並不需為了編寫正確的代碼而要考慮變量的逃逸行為,要記住的是,逃逸的變量需要額外分配內存,同時對性能的優化可能會產生細微的影響。
分析閉包:
由於在定義Test( )函數時指定了返回的類型是一個匿名函數,並且該匿名函數返回的類型是整型。所以在Test( )函數中定義了一個匿名函數,並且將整個匿名函數返回,匿名函數返回的是整型。在main( )函數中定義了一個變量f,該變量的類型是匿名函數,f( )表示調用執行匿名函數。最終執行完成后發現,實現了數字的累加。雖然Test()已經返回了,但是返回的值:func()還在全局變量中使用,三次調用 f(),因此返回值會保存在堆上,即使棧釋放了內存資源,但func()保存在堆中,數據不會釋放。
因為匿名函數(閉包),有一個很重要的特點:
它不關心這些捕獲了的變量和常量是否已經超出了作用域,所以只有閉包還在使用它,這些變量就還會存在。