這篇文章主要介紹了golang逃逸的示例分析,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
10年積累的網(wǎng)站建設、成都網(wǎng)站設計經(jīng)驗,可以快速應對客戶對網(wǎng)站的新想法和需求。提供各種問題對應的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡服務。我雖然不認識你,你也不認識我。但先網(wǎng)站設計后付款的網(wǎng)站建設流程,更有湞江免費網(wǎng)站建設讓你可以放心的選擇與我們合作。
垃圾回收是Go的一個很方便的特性--其自動的內存管理使代碼更整潔,同時減少內存泄漏的可能性。但是,由于垃圾回收需要周期性的停止程序從而去收集不用的對象,不可避免的會增加額外開銷。Go編譯器是智能的,它會自動決定一個變量是應該分配在堆上從而在將來便于回收,還是直接分配到函數(shù)的??臻g。對于分配到棧上的變量,其與分配到堆上的變量不同之處在于:隨著函數(shù)的返回,??臻g會被銷毀,從而棧上的變量被直接銷毀,不需要額外的垃圾回收開銷。
Go的逃逸分析相對于Java虛擬機的HotSpot來說更為基礎?;疽?guī)則就是,如果一個變量的引用從聲明它的函數(shù)中返出去了,則發(fā)生“逃逸”,因為它有可能在函數(shù)外被別的內容使用,所以必須分配到堆上。如下幾種情況會比較復雜:
函數(shù)調用其他函數(shù)
引用作為結構體的成員變量
切片和映射
Cgo指向變量的指針
為了實現(xiàn)逃逸分析,Go會在編譯階段構造函數(shù)調用關系圖,同時跟蹤入?yún)⒑头祷刂档牧鞒?。一個函數(shù)如果只是引用一個參數(shù),但這個引用并沒有返出函數(shù)的話,這個變量也不會逃逸。如果一個函數(shù)返回了一個引用,但是這個引用被棧中的其他函數(shù)解除或者沒有返回此引用,則也不會逃逸。為了論證幾個例子,可以在編譯時加-gcflags '-m'
參數(shù),這個參數(shù)會打印逃逸分析的詳細信息:
package main type S struct {} func main() { var x S _ = identity(x) } func identity(x S) S { return x }
你可以執(zhí)行go run -gcflags '-m -l'
(注:原文中略了go代碼文件名)來編譯這個代碼,-l參數(shù)是防止函數(shù)identity
被內聯(lián)(換個時間再討論內聯(lián)這個話題)。你將會看到?jīng)]有任何輸出!Go使用值傳遞,所以main
函數(shù)中的x
這個變量總是會被拷貝到函數(shù)identity
的??臻g。通常情況下沒有使用引用的代碼都是通過??臻g來分配內存。所以不涉及逃逸分析。下面試下困難一點的:
package main type S struct {} func main() { var x S y := &x _ = *identity(y) } func identity(z *S) *S { return z }
其對應的輸出是:
./escape.go:11: leaking param: z to result ~r1 ./escape.go:7: main &x does not escape
第一行顯示了變量z
的“流經(jīng)”:入?yún)⒅苯幼鳛榉祷刂捣祷亓恕5呛瘮?shù)identity
沒有取走z
這個引用,所以沒有發(fā)生變量逃逸。在main
函數(shù)返回后沒有任何對x
的引用存在,所以x
這個變量可以在main
函數(shù)的棧空間進行內存分配。
第三次實驗:
package main type S struct {} func main() { var x S _ = *ref(x) } func ref(z S) *S { return &z }
其輸出為:
./escape.go:10: moved to heap: z ./escape.go:11: &z escapes to heap
現(xiàn)在有了逃逸發(fā)生。記住Go是值傳遞的,所以z
是對變量x
的一個拷貝。函數(shù)ref
返回一個對z
的引用,所以z
不能在棧中分配,否則當函數(shù)ref
返回時,引用會指向何處呢?于是它逃逸到了堆中。其實執(zhí)行完ref
返回到main
函數(shù)中后,main
函數(shù)丟棄了這個引用而不是解除引用,但是Go的逃逸分析還不夠機智去識別這種情況。
值得注意的是,在這種情況下,如果我們不停止引用,編譯器將內聯(lián)ref
。
如果結構體成員定義的是引用又會怎樣呢?
package main type S struct { M *int } func main() { var i int refStruct(i) } func refStruct(y int) (z S) { z.M = &y return z }
其輸出為:
./escape.go:12: moved to heap: y ./escape.go:13: &y escapes to heap
在這種情況下,盡管引用是結構體的成員,但Go仍然會跟蹤引用的流向。由于函數(shù)refStruct
接受引用并將其返回,因此y
必須逃逸。對比如下這個例子:
package main type S struct { M *int } func main() { var i int refStruct(&i) } func refStruct(y *int) (z S) { z.M = y return z }
其輸出為:
./escape.go:12: leaking param: y to result z ./escape.go:9: main &i does not escape
盡管在main
函數(shù)中對i
變量做了引用操作,并傳遞到了函數(shù)refStruct
中,但是這個引用的范圍沒有超出其聲明它的棧空間。這和之前的那個程序語義上有細微的差別,這個會更高效:在上一個程序中,變量i
必須分配在main
函數(shù)的棧中,然后作為參數(shù)拷貝到函數(shù)refStruct
中,并將拷貝的這一份分配在堆上。而在這個例子中,i
僅被分配一次,然后將引用到處傳遞。
再來看一個有點彎彎繞的例子:
package main type S struct { M *int } func main() { var x S var i int ref(&i, &x) } func ref(y *int, z *S) { z.M = y }
其輸出為:
./escape.go:13: leaking param: y ./escape.go:13: ref z does not escape ./escape.go:9: moved to heap: i ./escape.go:10: &i escapes to heap ./escape.go:10: main &x does not escape
問題在于,y
被賦值給了一個入?yún)⒔Y構體的成員。Go并不能追溯這種關系(go只能追溯輸入直接流向輸出),所以逃逸分析失敗了,所以變量只能分配到堆上。由于Go的逃逸分析的局限性,許多變量會被分配到堆上,請參考此鏈接,這里面記錄了許多案例(從Go1.5開始)。
最后,來看下映射和切片是怎樣的呢?請記住,切片和映射實際上只是具有指向堆內存的指針的Go結構:slice
結構是暴露在reflect
包中(SliceHeader
)。map
結構就更隱蔽了:存在于hmap。如果這些結構體不逃逸,將會被分配到棧上,但是其底層的數(shù)組或者哈希桶中的實際數(shù)據(jù)會被分配到堆上去。避免這種情況的唯一方法是分配一個固定大小的數(shù)組(例如[10000]int
)。
如果你剖析過你的程序堆使用情況(https://blog.golang.org/pprof
),并且想減少垃圾回收的消耗,可以將頻繁分配到堆上的變量移到棧上,可能會有較好的效果。進一步研究HotSpot JVM是如何進行逃逸分析的會是一個不錯的話題,可以參考這個鏈接,這個里面主要講解了棧分配,以及有關何時可以消除同步的檢測。
感謝你能夠認真閱讀完這篇文章,希望小編分享的“golang逃逸的示例分析”這篇文章對大家有幫助,同時也希望大家多多支持創(chuàng)新互聯(lián),關注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關知識等著你來學習!
本文題目:golang逃逸的示例分析
分享網(wǎng)址:http://jinyejixie.com/article2/ggshic.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄、微信小程序、建站公司、品牌網(wǎng)站建設、搜索引擎優(yōu)化、ChatGPT
聲明:本網(wǎng)站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)