golang內(nèi)存逃逸的學(xué)習(xí)筆記
逃逸分析( Escape analysis) 是指由編譯器決定內(nèi)存分配的位置, 不需要程序員指定。 函數(shù)中申請(qǐng)一個(gè)新的對(duì)象。
- 如果分配在棧中, 則函數(shù)執(zhí)行結(jié)束可自動(dòng)將內(nèi)存回收;
- 如果分配在堆中, 則函數(shù)執(zhí)行結(jié)束可交給GC( 垃圾回收) 處理;
內(nèi)存逃逸策略
每當(dāng)函數(shù)中申請(qǐng)新的對(duì)象, 編譯器會(huì)跟據(jù)該對(duì)象是否被函數(shù)外部引用來決定是否逃逸:
- 如果函數(shù)外部沒有引用, 則優(yōu)先放到棧中
- 如果函數(shù)外部存在引用, 則必定放到堆中;
注意, 對(duì)于函數(shù)外部沒有引用的對(duì)象, 也有可能放到堆中, 比如內(nèi)存過大超過棧的存儲(chǔ)能力。
逃逸場(chǎng)景分析
指針逃逸
Go可以返回局部變量指針, 這其實(shí)是一個(gè)典型的變量逃逸案例, 示例代碼如下:
package main type Student struct { Name string age int } func NewStudent(name string, age int) *Student { stu := new(Student) stu.Name = name stu.age = age return stu } func main() { NewStudent("abcd", 24) }
函數(shù)NewStudent()內(nèi)部stu為局部變量, 其值通過函數(shù)返回值返回, stu本身為一指針, 其指向的內(nèi)存地址不會(huì)是棧而是堆, 這就是典型的逃逸案例。
通過編譯參數(shù)-gcflags=-m可以查年編譯過程中的逃逸分析:
運(yùn)行結(jié)果: 請(qǐng)注意運(yùn)行結(jié)果中出現(xiàn)了escapes to heap,也就是發(fā)生了內(nèi)存逃逸現(xiàn)象。
??臻g不足逃逸
分析一下下面代碼會(huì)不會(huì)產(chǎn)生內(nèi)存逃逸現(xiàn)象
package main type Student struct { Name string age int } func Slice() { s := make([]int, 1000, 1000) for index, _ := range s { s[index] = index } } func main() { Slice() }
上面代碼Slice()函數(shù)中分配了一個(gè)1000個(gè)長(zhǎng)度的切片, 是否逃逸取決于??臻g是否足夠大。 直接查看編譯提示, 如下可見并沒有發(fā)生逃逸:
但是如果長(zhǎng)度擴(kuò)大10倍呢?那情況會(huì)怎么樣?
package main func Slice() { s := make([]int, 10000, 10000) for index, _ := range s { s[index] = index } } func main() { Slice() }
結(jié)果:
我們發(fā)現(xiàn)當(dāng)切片長(zhǎng)度擴(kuò)大到10000時(shí)就會(huì)逃逸。實(shí)際上當(dāng)??臻g不足以存放當(dāng)前對(duì)象時(shí)或無(wú)法判斷當(dāng)前切片長(zhǎng)度時(shí)會(huì)將對(duì)象分配到堆中
動(dòng)態(tài)類型逃逸
很多函數(shù)參數(shù)為interface類型, 比如fmt.Println(a …interface{}), 編譯期間很難確定其參數(shù)的具體類型,也人產(chǎn)生逃逸。 如下代碼所示:
package main import "fmt" func main() { s := "abcd" fmt.Println(s) }
結(jié)果:上述代碼s變量只是一個(gè)string類型變量, 調(diào)用fmt.Println()時(shí)會(huì)產(chǎn)生逃逸。
閉包引用對(duì)象逃逸
相信刷題的同學(xué)對(duì)這代碼是十分的熟悉:
package main import "fmt" func Fibonacci() func() int { a, b := 0, 1 return func() int { a, b = b, a + b return a } } func main() { res := Fibonacci() for i := 0; i < 10; i++ { fmt.Printf("Print Result is %d\n" , res()) } }
這段代碼的運(yùn)行結(jié)果如下:
但是Fibonacci()函數(shù)中原本屬于局部變量的a和b由于閉包的引用, 不得不將二者放到堆上, 以致產(chǎn)生逃逸:
總結(jié)
- 棧上分配內(nèi)存比在堆中分配內(nèi)存有更高的效率
- 棧上分配的內(nèi)存不需要GC處理
- 堆上分配的內(nèi)存使用完畢會(huì)交給GC處理
- 逃逸分析目的是決定內(nèi)分配地址是棧還是堆
- 逃逸分析在編譯階段完成
到此這篇關(guān)于golang內(nèi)存逃逸的學(xué)習(xí)筆記的文章就介紹到這了,更多相關(guān)Golang內(nèi)存逃逸 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語(yǔ)言strconv包實(shí)現(xiàn)字符串和數(shù)值類型的相互轉(zhuǎn)換
這篇文章主要介紹了Go語(yǔ)言strconv包實(shí)現(xiàn)字符串和數(shù)值類型的相互轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03golang小游戲開發(fā)實(shí)戰(zhàn)之飛翔的小鳥
這篇文章主要給大家介紹了關(guān)于golang小游戲開發(fā)實(shí)戰(zhàn)之飛翔的小鳥的相關(guān)資料,,本文可以帶你你從零開始,一步一步的開發(fā)出這款小游戲,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-03-03Golang中切片長(zhǎng)度和容量的區(qū)別示例詳解
切片長(zhǎng)度與容量在Go中很常見,切片長(zhǎng)度是切片中可用元素的數(shù)量,而切片容量是從切片中第一個(gè)元素開始計(jì)算的底層數(shù)組中的元素?cái)?shù)量,這篇文章主要給大家介紹了關(guān)于Golang中切片長(zhǎng)度和容量區(qū)別的相關(guān)資料,需要的朋友可以參考下2024-01-01Go語(yǔ)言題解LeetCode455分發(fā)餅干示例詳解
這篇文章主要為大家介紹了Go語(yǔ)言題解LeetCode455分發(fā)餅干示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Golang?json?庫(kù)中的RawMessage功能原理
今天我們來學(xué)習(xí)一個(gè) Golang 官方 json 庫(kù)提供了一個(gè)經(jīng)典能力RawMessage,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05使用Go實(shí)現(xiàn)偽靜態(tài)URL重寫功能
在Web開發(fā)中,偽靜態(tài)URL已成為優(yōu)化網(wǎng)站架構(gòu)和提升SEO的常用技術(shù)手段,偽靜態(tài)URL是一種介于動(dòng)態(tài)URL和靜態(tài)URL之間的解決方案,本文給大家介紹了如何使用Go實(shí)現(xiàn)偽靜態(tài)URL重寫功能,需要的朋友可以參考下2024-08-08