Go 語言逃逸分析
大家好,我是 frank。
歡迎大家點擊標題下方藍色文字「Golang 語言開發棧」關注公衆號。
設爲星標,第一時間接收推送文章。
文末掃碼,加羣一起學 Golang 語言。
01 介紹
熟悉 C / C++ 的讀者朋友們應該都知道一個進程(應用程序)的虛擬內存空間劃分爲棧內存區和堆內存區。
棧內存區上對象的內存空間是自動分配和銷燬的,使用者無需關心。但是,堆內存區上對象的內存空間是需要使用者自己管理,無形中增加了使用者的心智負擔。
因此,一些高級語言會支持垃圾回收(GC),降低使用者內存管理的心智負擔。支持垃圾回收的語言可以自動管理堆內存區上對象的內存空間。
Go 語言編譯器負責決定把對象分配到棧上或堆上,比如一個對象在函數退出後就不可達(沒有其他對象引用該對象)時,那就將該對象分配到棧上,反之,則分配到堆上。
如果一個對象被分配到堆上,就需要 Go 的垃圾回收管理該對象的內存空間。但是,垃圾回收是有代價的,它會佔用系統開銷。
所以,爲了更大限度地降低垃圾回收佔用的系統資源,提升應用程序本身可使用的系統資源,使用者就需要儘量減少堆內存分配,儘量多地使應用程序使用棧內存分配,儘量避免 Go 編譯器通過逃逸分析優化後被分配到棧內存的對象逃逸到堆內存。
02 查看對象是否發生逃逸
Go 語言工具鏈提供了查看對象是否逃逸的方法,我們在執行 go build
時,配合使用參數 -gcflags
開啓編譯器支持的額外功能,例如:
go build -gcflasg '-m -m -l' main.go
-
-m 用於輸出編譯器的執行細節,包括逃逸分析的執行。
-
-l 用於禁用內聯優化。
我們通過使用 Go 語言工具鏈對一段簡單的示例代碼進行查看對象是否發生逃逸。
func main() {
sum(1, 2)
}
func sum(a, b int) *int {
res := a + b
return &res
}
輸出結果:
go build -gcflags '-m -m -l' main.go
# command-line-arguments
./main.go:8:2: res escapes to heap:
./main.go:8:2: flow: ~r0 = &res:
./main.go:8:2: from &res (address-of) at ./main.go:9:9
./main.go:8:2: from return &res (return) at ./main.go:9:2
./main.go:8:2: moved to heap: res
閱讀上面這段代碼,我們發現 sum
函數中的變量 res
逃逸到堆,也就是說 Go 編譯器通過逃逸分析,決定將變量 res
分配到堆空間。
03 逃逸分析的作用
Go 語言編譯器通過逃逸分析優化,將對象合理分配到棧空間和堆空間。
因爲棧內存分配比堆內存分配更快,所以 Go 語言在編譯時通過逃逸分析優化將不會發生逃逸的對象優先分配到棧空間。
因此,不僅降低堆空間內存分配的開銷,同時,也可以降低垃圾回收佔用的系統資源。
04 總結
本文我們介紹 Go 語言逃逸分析,它可以幫助使用者合理分配對象的內存空間。
我們知道分配到堆內存空間的對象,會導致 Go 執行垃圾回收,而垃圾回收會佔用系統資源,降低應用程序本身可使用的系統資源。
所以,我們在實際項目開發中,可以藉助 Go 工具鏈分析對象是否會發生逃逸,儘量避免一些不必要的對象逃逸。
參考資料:
-
https://en.wikipedia.org/wiki/Escape_analysis
-
https://go.dev/doc/faq#stack_or_heap
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/34cmyuPOjlhAQm6zYhBIsg