理解golang中的內(nèi)存逃逸

在理解內(nèi)存逃逸之前,我們需要先了解下啥是堆內(nèi)存和棧內(nèi)存

堆和棧

棧內(nèi)存:由編譯器自動管理,自動分配管理,存放局部變量,函數(shù)參數(shù)等
堆內(nèi)存:一般需要人為手動管理,手動申請、分配和釋放等,在c/c++中,是需要通過new/malloc進(jìn)行動態(tài)分配內(nèi)存,而golang中,一般是垃圾回收進(jìn)行處理。

優(yōu)缺點:
棧內(nèi)存分配快速,一般退出函數(shù)后就自動回收了,開銷比較小,棧內(nèi)存不需要gc參與
堆內(nèi)存分配代價大,在golang中,堆內(nèi)存的回收需要垃圾回收(也就是gc)進(jìn)行參與,而垃圾回收則是需要比較大的系統(tǒng)開銷。

內(nèi)存逃逸

在編寫golang程序代碼時,某些變量和數(shù)據(jù)的生命周期超出了原始作用域的情況就稱為內(nèi)存逃逸。當(dāng)內(nèi)存逃逸時,變量和數(shù)據(jù)內(nèi)存會從棧區(qū)逃逸到堆區(qū)。

逃逸的情況

1、變量超出作用域:
在函數(shù)內(nèi)聲明的變量,如果在函數(shù)返回時仍然被引用,就會導(dǎo)致內(nèi)存逃逸。分配在堆區(qū),保證函數(shù)返回時繼續(xù)可以用
2、引用外部變量
在函數(shù)內(nèi)部引用了外部變量,可能導(dǎo)致內(nèi)存逃逸。編譯器會無法確定變量的生命周期,可能分配到堆區(qū)
3、使用閉包
閉包可以捕獲外部變量值,這些變量的生命周期可能超出了閉包本身的生命周期

檢測方法

go build增加 -gcflags="-m -l",如下

go build -gcflags="-m -l" test.go

如下顯示表示逃逸到了堆區(qū)


image.png

demo樣例

1、函數(shù)返回繼續(xù)引用變量
NewImCache函數(shù)返回之后,局部變量c仍然被引用,因此逃逸到了堆區(qū)

package main
import "fmt"
var cache = NewImCache()
func NewImCache() *imCache {
    c := new(imCache)
    return c
}
func (i *imCache) GetNum() int {
    return 1
}
type imCache struct {
}
func main() {
    res := cache.GetNum()
    fmt.Print(res)
}

2、閉包捕獲
閉包函數(shù)內(nèi)部捕獲了外部變量count。但是閉包函數(shù)的生命周期超出了包含它的函數(shù),所以逃逸到堆區(qū)

package main

import "fmt"

func main() {
    res := counter()
    fmt.Println(res())
}

func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

3、使用協(xié)程
協(xié)程中引用了外部變量res,導(dǎo)致data逃逸到了堆區(qū)

package main

import "fmt"

func main() {
    res := 1
    go func() {
        fmt.Println(res)
    }()
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容