本貼轉(zhuǎn)自https://www.cnblogs.com/itbsl/p/10476674.html
如侵權(quán)击碗,請(qǐng)聯(lián)系QQ359152155刪除。
Go變量逃逸分析
逃逸分析
決定一個(gè)變量是分配在堆上還是棧上啡浊。
逃逸分析
這種操作把變量合理地分配到它該去的地方醋拧,即使你是用new申請(qǐng)到的內(nèi)存畜晰,如果我發(fā)現(xiàn)你竟然在退出函數(shù)后就沒有用了西土,那么就把你丟到棧上陆错,畢竟棧上的內(nèi)存分配比堆上快得多魄揉;反之剪侮,即使你表面上只是一個(gè)普通的變量,但是經(jīng)過逃逸分析后發(fā)現(xiàn)在退出函數(shù)之后還有其他地方在引用洛退,那我就把你分配到堆上瓣俯。
如果變量都分配到堆上,堆不像棻樱可以自動(dòng)清理彩匕,它會(huì)引起Go頻繁地進(jìn)行垃圾回收,而垃圾回收會(huì)占用比較大的系統(tǒng)開銷媒区。
通過逃逸分析驼仪,可以盡量把哪些不需要分配到對(duì)上的變量直接分配到棧上,堆上的變量少了袜漩,會(huì)減輕分配堆內(nèi)存的開銷绪爸,同時(shí)也會(huì)減少GC的壓力,提高程序的運(yùn)行速度宙攻。
逃逸分析是怎么完成的
Go逃逸分析的最基本原則是:如果一個(gè)函數(shù)返回堆一個(gè)變量的引用奠货,那么它就會(huì)發(fā)生逃逸。
編譯器會(huì)分析代碼的特征和代碼生命周期座掘,Go中的變量只有在編譯器可以證明在函數(shù)返回后不會(huì)再被引用递惋,才會(huì)分配到棧上,其他情況下都是分配到堆上雹顺。
逃逸分析實(shí)例
Go提供了相關(guān)的命令丹墨,可以查看變量是否發(fā)生逃逸。
用下面這個(gè)例子:
package main
import "fmt"
func foo() *int {
t := 3
return &t;
}
func main() {
x := foo()
fmt.Println(*x)
}
foo函數(shù)返回一個(gè)局部變量的指針嬉愧,main函數(shù)里變量x接收它贩挣。執(zhí)行如下命令:
go build -gcflags="-m -l" main.go
加-l
是為了不讓foo函數(shù)被內(nèi)聯(lián),得到如下輸出:
# command-line-arguments
.\hello.go:6:5: moved to heap: t
.\hello.go:12:16: ... argument does not escape
.\hello.go:12:17: *x escapes to heap
foo函數(shù)里的變量t逃逸了没酣,和我們預(yù)想的一致王财。讓我們不解的是為什么main函數(shù)里的*x也逃逸了?這是因?yàn)閒mt.Println()函數(shù)的參數(shù)是interface類型裕便,編譯期間很難確定其參數(shù)的具體類型绒净,也會(huì)發(fā)生逃逸。
總結(jié)
堆上動(dòng)態(tài)分配內(nèi)存比棧上靜態(tài)分配內(nèi)存偿衰,開銷大很多挂疆。
Go編譯器會(huì)在編譯器分析變量的生命周期改览,編譯器會(huì)根據(jù)變量是否在函數(shù)返回后仍舊被引用來決定是否逃逸。對(duì)于Go程序原來說缤言,編譯器的這些逃逸分析規(guī)則不需要掌握宝当,我們只需通過`go build -gcflags="-m"命令來觀察變量逃逸情況就行了。
不要盲目使用變量的指針作為函數(shù)參數(shù)胆萧,雖然它會(huì)減少復(fù)制操作庆揩,但其實(shí)當(dāng)參數(shù)為變量自身的時(shí)候,復(fù)制是在站上完成的操作跌穗,開銷遠(yuǎn)比變量逃逸后動(dòng)態(tài)地在堆上分配內(nèi)存少的多订晌。
最后,盡量寫出少一些逃逸的代碼蚌吸,提升程序的運(yùn)行效率锈拨。