每天一個知識點:Golang 內(nèi)存逃逸

什么是內(nèi)存逃逸楼雹?

在程序中详幽,每個函數(shù)塊都會有自己的內(nèi)存區(qū)域用來存自己的局部變量(內(nèi)存占用少)、返回地址恢共、返回值之類的數(shù)據(jù)战秋,這一塊內(nèi)存區(qū)域有特定的結(jié)構(gòu)和尋址方式,尋址起來十分迅速讨韭,開銷很少脂信。這一塊內(nèi)存地址稱為棧。棧是線程級別的透硝,大小在創(chuàng)建的時候已經(jīng)確定狰闪,當變量太大的時候,會"逃逸"到堆上濒生,這種現(xiàn)象稱為內(nèi)存逃逸埋泵。簡單來說,局部變量通過堆分配和回收,就叫內(nèi)存逃逸丽声。

逃逸是如何產(chǎn)生的礁蔗?

如果一個函數(shù)返回對一個變量的引用,那么它就會發(fā)生逃逸雁社。即任何時候浴井,一個值被分享到函數(shù)棧范圍之外,它都會在堆上被重新分配霉撵。在這里有一個例外磺浙,就是如果編譯器可以證明在函數(shù)返回后不會再被引用的,那么就會分配到棧上徒坡,這個證明的過程叫做逃逸分析屠缭。

總結(jié):

  • 如果函數(shù)外部沒有引用,則優(yōu)先放到棧中崭参;
  • 如果函數(shù)外部存在引用呵曹,則必定放到堆中;

內(nèi)存逃逸的危害

堆是一塊沒有特定結(jié)構(gòu)何暮,也沒有固定大小的內(nèi)存區(qū)域奄喂,可以根據(jù)需要進行調(diào)整。全局變量海洼,內(nèi)存占用較大的局部變量跨新,函數(shù)調(diào)用結(jié)束后不能立刻回收的局部變量都會存在堆里面。變量在堆上的分配和回收都比在棧上開銷大的多坏逢。對于 go 這種帶 GC 的語言來說域帐,會增加 gc 壓力,同時也容易造成內(nèi)存碎片(采用分區(qū)式存儲管理的系統(tǒng)是整,在儲存分配過程中產(chǎn)生的肖揣、不能供用戶作業(yè)使用的主存里的小分區(qū)稱成“內(nèi)存碎片”。內(nèi)存碎片分為內(nèi)部碎片和外部碎片)浮入。

逃逸的例子

  • 向channel發(fā)送指針數(shù)據(jù)龙优。因為在編譯時,不知道channel中的數(shù)據(jù)會被哪個 goroutine 接收事秀,因此編譯器沒法知道變量什么時候才會被釋放彤断,因此只能放入堆中。
package main
func main() {
ch := make(chan int, 1)
x := 5
ch <- x  // x 不發(fā)生逃逸易迹,因為只是復(fù)制的值
ch1 := make(chan *int, 1)
y := 5
py := &y
ch1 <- py  // y 逃逸宰衙,因為 y 地址傳入了chan中,編譯時無法確定什么時候會被接收睹欲,所以也無法在函數(shù)返回后回收 y
}
  • 局部變量在函數(shù)調(diào)用結(jié)束后還被其他地方使用供炼,比如函數(shù)返回局部變量指針或閉包中引用包外的值。因為變量的生命周期可能會超過函數(shù)周期,因此只能放入堆中劲蜻。
func Foo () func (){
x := 5 // x發(fā)生逃逸陆淀,因為在Foo調(diào)用完成后,被閉包函數(shù)用到先嬉,還不能回收轧苫,只能放到堆上存放
return func () {
x += 1
}
}
func main() {
inner := Foo()
inner()
}
  • 在 slice 或 map 中存儲指針。比如 []*string疫蔓,其后面的數(shù)組可能是在棧上分配的含懊,但其引用的值還是在堆上。
package main
func main() {
  var x int
  x = 10
  var ls []*int
  ls = append(ls, &x) // x發(fā)生逃逸衅胀,ls存儲的是指針岔乔,所以ls底層的數(shù)組雖然在棧存儲,但x本身卻是逃逸到堆上
}
  • 切片擴容后長度太大滚躯,導(dǎo)致棾牛空間不足,逃逸到堆上掸掏。
func main() {
var x int
x = 10
var ls []*int
ls = append(ls, &x) // x發(fā)生逃逸茁影,ls存儲的是指針,所以ls底層的數(shù)組雖然在棧存儲丧凤,但x本身卻是逃逸到堆上
}
  • 在 interface 類型上調(diào)用方法募闲。 在 interface 類型上調(diào)用方法時會把interface變量使用堆分配, 因為方法的真正實現(xiàn)只能在運行時知道愿待。
package main
type foo interface {
  fooFunc()
}
type foo1 struct{}
func (f1 foo1) fooFunc() {}
func main() {
var f foo
f = foo1{}
f.fooFunc()  // 調(diào)用方法時浩螺,f發(fā)生逃逸,因為方法是動態(tài)分配的
}

如何避免內(nèi)存逃逸仍侥?

  • 對于小型的數(shù)據(jù)要出,使用傳值而不是傳指針,避免內(nèi)存逃逸访圃。
    • 對于需要修改原對象值厨幻,或占用內(nèi)存比較大的結(jié)構(gòu)體相嵌,選擇傳指針腿时。
    • 對于只讀的占用內(nèi)存較小的結(jié)構(gòu)體,直接傳值能夠獲得更好的性能饭宾。
  • 盡量避免使用長度不固定的 slice 切片批糟,因為在編譯期無法確定切片長度,只能將切片使用堆分配看铆。
  • 對于性能要求比較高且訪問頻次比較高的函數(shù)調(diào)用徽鼎,謹慎使用 interface 調(diào)用方法。

參考

簡單聊聊內(nèi)存逃逸 | 劍指offer - golang
Golang內(nèi)存逃逸是什么?怎么避免內(nèi)存逃逸否淤?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末悄但,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子石抡,更是在濱河造成了極大的恐慌檐嚣,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啰扛,死亡現(xiàn)場離奇詭異嚎京,居然都是意外死亡,警方通過查閱死者的電腦和手機隐解,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門鞍帝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人煞茫,你說我怎么就攤上這事帕涌。” “怎么了续徽?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵宵膨,是天一觀的道長。 經(jīng)常有香客問我炸宵,道長辟躏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任土全,我火速辦了婚禮捎琐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘裹匙。我一直安慰自己瑞凑,他們只是感情好,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布概页。 她就那樣靜靜地躺著籽御,像睡著了一般。 火紅的嫁衣襯著肌膚如雪惰匙。 梳的紋絲不亂的頭發(fā)上技掏,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機與錄音项鬼,去河邊找鬼哑梳。 笑死,一個胖子當著我的面吹牛绘盟,可吹牛的內(nèi)容都是我干的鸠真。 我是一名探鬼主播悯仙,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼吠卷!你這毒婦竟也來了锡垄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤祭隔,失蹤者是張志新(化名)和其女友劉穎偎捎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體序攘,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡茴她,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了程奠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丈牢。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瞄沙,靈堂內(nèi)的尸體忽然破棺而出己沛,到底是詐尸還是另有隱情,我是刑警寧澤距境,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布申尼,位于F島的核電站,受9級特大地震影響垫桂,放射性物質(zhì)發(fā)生泄漏师幕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一诬滩、第九天 我趴在偏房一處隱蔽的房頂上張望霹粥。 院中可真熱鬧,春花似錦疼鸟、人聲如沸后控。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽浩淘。三九已至,卻和暖如春吴攒,著一層夾襖步出監(jiān)牢的瞬間张抄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工舶斧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留欣鳖,地道東北人。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓茴厉,卻偏偏與公主長得像泽台,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子矾缓,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 為什么要盡量避免內(nèi)存逃逸怀酷? 因為如果變量的內(nèi)存發(fā)生逃逸,它的生命周期就是不可知的嗜闻,其會被分配到堆上蜕依,而堆上分配內(nèi)存...
    dongzd閱讀 1,307評論 0 1
  • 參考資源一參考資源二參考資源三 對于手動管理內(nèi)存的語言,比如 C/C++琉雳,調(diào)用著名的malloc和new函數(shù)可以在...
    簾外五更風閱讀 1,079評論 0 0
  • 問題 知道golang的內(nèi)存逃逸嗎翠肘?什么情況下會發(fā)生內(nèi)存逃逸檐束? 怎么答 golang程序變量會攜帶有一組校驗數(shù)據(jù),...
    9號閱讀 695評論 0 1
  • 一束倍、類加載機制 類加載就是虛擬機把Class文件加載到內(nèi)存被丧,并對數(shù)據(jù)進行校驗,解析和初始化绪妹,形成可以虛擬機直接使用...
    xiaoqunzi233閱讀 285評論 0 0
  • 一甥桂、我們說內(nèi)存逃逸時在說什么 問,內(nèi)存逃逸是干什么的答邮旷,內(nèi)存逃逸分析是編譯器在編譯優(yōu)化時黄选,用來決定變量應(yīng)該分配在堆...
    銀角代王閱讀 1,426評論 0 4