<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在一段程式中,每一個函數都會有自己的記憶體區域分配自己的區域性變數,返回值,這些記憶體會由編譯器在棧中進行分配,每一個函數會分配一個棧幀,在函數執行結束後銷燬,但是有些變數我們想在函數執行結束後仍然使用,就需要把這個變數分配在堆上,這種從“棧”上逃逸到“堆”上的現象叫做記憶體逃逸
雖然Go語言引入的Gc,GC機制會對堆上的物件進行管理,當某個物件不可達(沒有其他物件參照他),他將會被回收。雖然GC可以降低工作人員負擔,但是GC也會給程式帶來效能損耗,當堆記憶體上有大量的堆記憶體物件,就會給GC很大的壓力,雖然Go語言使用的是標記清除演演算法,並且在此基礎上使用了三色標記法和寫屏障技術,但是我們在堆上分配大量記憶體,仍然會對GC造成很大壓力,Go引入了逃逸分析,就是想減少堆記憶體的分配,可以在棧分配的記憶體儘量分配在棧上
逃逸分析就是在程式編譯階段根據程式碼中的資料流,對程式碼中哪些變數需要在棧上分配,哪些需要在物件分配的靜態分析方法,堆和棧相比,堆適合分配不可預知大小的記憶體,但是付出代價是分配速度慢,容易產生碎片,棧分配十分快,棧分配只需要兩個指令“Push”和"Release"分配和釋放,而且堆分配需要先找一塊適合大小的記憶體塊分配,需要垃圾回收釋放,所以逃逸分析可以更好的做記憶體分配
Go語言的逃逸分析
src/cmd/compile/internal/gc/escape.go
既然逃逸分析是在編譯階段進行的,那我們就可以通過go build -gcflga '-m -m l'檢視逃逸分析結果
func Add(x,y int) *int { res := 0 res = x + y return &res } func main() { Add(1,2) }
.pointer.go:4:2: res escapes to heap:
.pointer.go:4:2: flow: ~r2 = &res:
.pointer.go:4:2: from &res (address-of) at .pointer.go:6:9
.pointer.go:4:2: from return &res (return) at .pointer.go:6:2
.pointer.go:4:2: moved to heap: res
函數返回區域性變數是一個指標變數,函數Add執行結束,對應棧幀就會銷燬,但是參照返回到函數外部,如果我們外部解析地址,就會導致程式存取非法記憶體,所以經過編輯器分析過後將其在堆上分配
func main() { str := "荔枝" fmt.Println(str) }
E:GoStudysrcHighBaseEscape>go build -gcflags="-m -m -l" ./pointer.go
# command-line-arguments
.pointer.go:20:13: str escapes to heap:
.pointer.go:20:13: flow: {storage for ... argument} = &{storage for str}:
.pointer.go:20:13: from str (spill) at .pointer.go:20:13
.pointer.go:20:13: from ... argument (slice-literal-element) at .pointer.go:20:13
.pointer.go:20:13: flow: {heap} = {storage for ... argument}:
.pointer.go:20:13: from ... argument (spill) at .pointer.go:20:13
.pointer.go:20:13: from fmt.Println(... argument...) (call parameter) at .pointer.go:20:13
.pointer.go:20:13: ... argument does not escape
.pointer.go:20:13: str escapes to heap
str是main的一個區域性變數,傳給 fmt.Printl()之後逃逸,因為fmt.Println()的入參是interface{}型別,如果引數為interface{},那麼編譯期間就很難確定引數型別
我們把程式碼改成這樣
func main() { str := "蘇珊" fmt.Println(&str) }
# command-line-arguments
.pointer.go:19:2: str escapes to heap:
.pointer.go:19:2: flow: {storage for ... argument} = &str:
.pointer.go:19:2: from &str (address-of) at .pointer.go:20:14
.pointer.go:19:2: from &str (interface-converted) at .pointer.go:20:14
.pointer.go:19:2: from ... argument (slice-literal-element) at .pointer.go:20:13
.pointer.go:19:2: flow: {heap} = {storage for ... argument}:
.pointer.go:19:2: from ... argument (spill) at .pointer.go:20:13
.pointer.go:19:2: from fmt.Println(... argument...) (call parameter) at .pointer.go:20:13
.pointer.go:19:2: moved to heap: str
.pointer.go:20:13: ... argument does not escape
這次str也逃逸到堆上面了,在堆上面進行分配,因為入參是interface,變數str的地址被以實參的方式傳入fmt.Println被裝箱到一個interface{}
裝箱的形參變數要在堆上分配,但是還需要儲存一個棧上的地址,這和之前說的第一條不符,所以str也會分配到堆上
func Increase() func() int { n := 0 return func() int { n++ return n } } func main() { in := Increase() fmt.Println(in()) // 1 }
E:GoStudysrcHighBaseEscape>go build -gcflags "-m -m -l" ./pointer.go
# command-line-arguments
.pointer.go:27:2: Increase capturing by ref: n (addr=false assign=true width=8)
.pointer.go:28:9: func literal escapes to heap:
.pointer.go:28:9: flow: ~r0 = &{storage for func literal}:
.pointer.go:28:9: from func literal (spill) at .pointer.go:28:9
.pointer.go:28:9: from return func literal (return) at .pointer.go:28:2
.pointer.go:27:2: n escapes to heap:
.pointer.go:27:2: flow: {storage for func literal} = &n:
.pointer.go:27:2: from n (captured by a closure) at .pointer.go:29:3
.pointer.go:27:2: from n (reference) at .pointer.go:29:3
.pointer.go:27:2: moved to heap: n
.pointer.go:28:9: func literal escapes to heap
.pointer.go:36:16: in() escapes to heap:
.pointer.go:36:16: flow: {storage for ... argument} = &{storage for in()}:
.pointer.go:36:16: from in() (spill) at .pointer.go:36:16
.pointer.go:36:16: from ... argument (slice-literal-element) at .pointer.go:36:13
.pointer.go:36:16: flow: {heap} = {storage for ... argument}:
.pointer.go:36:16: from ... argument (spill) at .pointer.go:36:13
.pointer.go:36:16: from fmt.Println(... argument...) (call parameter) at .pointer.go:36:13
.pointer.go:36:13: ... argument does not escape
.pointer.go:36:16: in() escapes to heap
因為函數是指標型別,所以匿名函數當做返回值產生逃逸,匿名函數使用外部變數n,這個n會一直存在知道in被銷燬
import ( "math/rand" ) func LessThan8192() { nums := make([]int, 100) // = 64KB for i := 0; i < len(nums); i++ { nums[i] = rand.Int() } } func MoreThan8192(){ nums := make([]int, 1000000) // = 64KB for i := 0; i < len(nums); i++ { nums[i] = rand.Int() } } func NonConstant() { number := 10 s := make([]int, number) for i := 0; i < len(s); i++ { s[i] = i } } func main() { NonConstant() MoreThan8192() LessThan8192() }
# command-line-arguments
.pointer.go:43:14: make([]int, 100) does not escape
.pointer.go:51:14: make([]int, 1000000) escapes to heap:
.pointer.go:51:14: flow: {heap} = &{storage for make([]int, 1000000)}:
.pointer.go:51:14: from make([]int, 1000000) (too large for stack) at .pointer.go:51:14
.pointer.go:51:14: make([]int, 1000000) escapes to heap
.pointer.go:60:11: make([]int, number) escapes to heap:
.pointer.go:60:11: flow: {heap} = &{storage for make([]int, number)}:
.pointer.go:60:11: from make([]int, number) (non-constant size) at .pointer.go:60:11
.pointer.go:60:11: make([]int, number) escapes to heap
棧空間足夠不會發生逃逸,但是變數過大,已經超過棧空間,會逃逸到堆上
到此這篇關於淺談Golang記憶體逃逸 的文章就介紹到這了,更多相關Golang記憶體逃逸 內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45