亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

golang內(nèi)存逃逸分析

 更新時(shí)間:2025年06月09日 10:10:21   作者:瘋狂的程需猿  
本文主要介紹了golang內(nèi)存逃逸分析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、編譯器的逃逸分析

go語(yǔ)言編譯器會(huì)自動(dòng)決定把一個(gè)變量放在堆上還是放在棧上,編譯器會(huì)做逃逸分析,當(dāng)發(fā)現(xiàn)變量的作用域沒(méi)有跑出函數(shù)范圍(懸空指針),就可以在棧上,否則則必須分配在堆上。

這樣可以釋放程序員關(guān)于內(nèi)存的使用限制,更多的讓程序員關(guān)注于程序功能邏輯本身。

我們看如下代碼:

package main

func main() {
	// 打印返回的指針地址
	println(fool())
	// 0x1400008e000
}

//go:noinline    內(nèi)置的編譯指令,可以強(qiáng)制讓 Go 編譯器不對(duì)指定的函數(shù)進(jìn)行內(nèi)聯(lián)優(yōu)化
func fool() *int {
	var (
		a = 1
		b = 2
		c = 3
		d = 4
		e = 5
	)
	println(&a, &b, &c, &d, &e)
	// 0x14000066f38 0x14000066f30 0x1400008e000 0x14000066f28 0x14000066f20
	return &c
}

我們能看到**c**是返回給main 的局部變量,其中它的地址值是 0x1400008e000 很明顯與其他的 a b d e 不是連續(xù)的。

我們用go tool compile測(cè)試一下

 ~/workspace/test  go tool compile -m main.go
main.go:3:6: can inline main
main.go:14:3: moved to heap: c

果然,在編譯的時(shí)候,c 被編譯器判定為逃逸變量,將c 放在堆中開(kāi)辟

內(nèi)聯(lián): go編譯器會(huì)對(duì)一些小函數(shù)進(jìn)行內(nèi)聯(lián)優(yōu)化,以提升性能。內(nèi)聯(lián)優(yōu)化意味著函數(shù)的代碼會(huì)在調(diào)用處直接展開(kāi),而不是常規(guī)的函數(shù)調(diào)用。這就導(dǎo)致一些逃逸分析的行為發(fā)生變化,類(lèi)似上面那個(gè)代碼的內(nèi)存地址就會(huì)是連續(xù)的。

什么時(shí)候編譯器會(huì)進(jìn)行內(nèi)聯(lián)優(yōu)化?

  • 函數(shù)體較?。篏o編譯器更容易將體積較小的函數(shù)進(jìn)行內(nèi)聯(lián)
  • 無(wú)復(fù)雜控制結(jié)構(gòu):如果函數(shù)內(nèi)沒(méi)有復(fù)雜的循環(huán),條件分支等……內(nèi)聯(lián)的可能性更高
  • 函數(shù)參數(shù)和返回值簡(jiǎn)單:函數(shù)參數(shù)和返回值不過(guò)于復(fù)雜也有助于函數(shù)的內(nèi)聯(lián)

二、new的變量?jī)?nèi)存分配在棧還是堆上?

new 出來(lái)的變量,內(nèi)存一定是分配在堆上嗎?

還是原來(lái)的代碼,我們通過(guò)new 分開(kāi)來(lái)看看:

package main

func main() {
	// 打印返回的指針地址
	println(fool())
	// 0x1400001a0a0
}

//go:noinline    內(nèi)置的編譯指令,可以強(qiáng)制讓 Go 編譯器不對(duì)指定的函數(shù)進(jìn)行內(nèi)聯(lián)優(yōu)化
func fool() *int {
	var (
		a = new(int)
		b = new(int)
		c = new(int)
		d = new(int)
		e = new(int)
	)
	println(a, b, c, d, e)
	// 0x14000098f38 0x14000098f30 0x1400001a0a0 0x14000098f28 0x14000098f20
	return c
}

很明顯,c 的地址 0x1400001a0a0 依然和其他的不是連續(xù)的內(nèi)存空間,依然具備逃逸行為。所以這里不是分配在堆上的。

結(jié)論:

  • new 并不強(qiáng)制堆分配: 使用 new (T)分配的變量不一定分配在堆上,依然依賴(lài)于 Go 編譯器的逃逸分析結(jié)果。
  • 是否逃逸決定了內(nèi)存分配的位置:如果變量需要在函數(shù)作用域外使用(逃逸),則分配在堆上;如果可以在局部棧中管理,則分配在棧上。

三、逃逸規(guī)則

一般我們給一個(gè)引用類(lèi)對(duì)象中的引用類(lèi)成員進(jìn)行賦值,就可能會(huì)出現(xiàn)逃逸現(xiàn)象??梢岳斫鉃樵L問(wèn)一個(gè)引用對(duì)象實(shí)際上底層就是通過(guò)一個(gè)指針來(lái)間接的訪問(wèn)了,但是如果再訪問(wèn)里面的引用成員就會(huì)有第二次間接訪問(wèn),這樣操作這部分對(duì)象的話,就有可能會(huì)出現(xiàn)逃逸現(xiàn)象了。

Go 語(yǔ)言的引用類(lèi)型有:func(函數(shù)類(lèi)型)、 interface(接口類(lèi)型)、slice(切片類(lèi)型)、 map(字典類(lèi)型)、 channel(管道類(lèi)型)、 *(指針類(lèi)型) 等.

案例1

如果一個(gè)函數(shù)作為值傳遞給另一個(gè)函數(shù),或者被作為閉包使用,生命周期超出其原始作用域,則它會(huì)逃逸。

package main

func main() {
	foo()()
}

//go:noinline
func foo() func() {
	return func() {
		println("call")
	}
}

通過(guò)編譯看看逃逸分析:

~/workspace/test  go tool compile -m main.go
main.go:9:9: can inline foo.func1
main.go:9:9: func literal escapes to heap

能看到 發(fā)生了逃逸現(xiàn)象

案例2

對(duì)一個(gè)[]interface{} 類(lèi)型嘗試進(jìn)行賦值,必定出現(xiàn)逃逸

package main

//go:noinline
func main() {
	var a = []interface{}{"100", "1000"}
	a[0] = 10
}

逃逸分析:

 ~/workspace/test  go tool compile -m main.go
main.go:5:23: []interface {}{...} does not escape
main.go:5:24: "100" does not escape
main.go:5:31: "1000" does not escape
main.go:6:2: 10 escapes to heap

a[0]=10 發(fā)生了逃逸現(xiàn)象

案例3

map[string]interface{}類(lèi)型嘗試通過(guò)賦值,必定出現(xiàn)逃逸

package main

//go:noinline
func main() {
	var a = make(map[string]interface{})
	a["hello"] = "world"
	a["1"] = "1"
}

逃逸分析:

 ~/workspace/test  go tool compile -m main.go
main.go:5:14: make(map[string]interface {}) does not escape
main.go:6:2: "world" escapes to heap
main.go:7:2: "1" escapes to heap

a["hello"] = "world" a["1"] = "1" 分別都發(fā)生了逃逸

案例4

map[interface{}]interface{} 類(lèi)型嘗試通過(guò)賦值,會(huì)導(dǎo)致key 和 value 的賦值出現(xiàn)逃逸

package main

//go:noinline
func main() {
	var a = make(map[interface{}]interface{})
	a["hello"] = "world"
}

看看編譯結(jié)果:

 ~/workspace/test  go tool compile -m main.go
main.go:5:14: make(map[interface {}]interface {}) does not escape
main.go:6:2: "hello" escapes to heap
main.go:6:2: "world" escapes to heap

我們能看到,key 和 value 均發(fā)生了逃逸

案例5

map[string][]string數(shù)據(jù)類(lèi)型,賦值 []string 會(huì)發(fā)生逃逸

package main

//go:noinline
func main() {
	var a = make(map[string][]string)
	a["hello"] = []string{"word1"}
}

通過(guò)逃逸分析發(fā)現(xiàn):

 ~/workspace/test  go tool compile -m main.go
main.go:5:14: make(map[string][]string) does not escape
main.go:6:23: []string{...} escapes to heap

[]string{…} 切片發(fā)生了逃逸

案例6

[]*int 數(shù)據(jù)類(lèi)型,賦值的右側(cè)會(huì)發(fā)生逃逸

package main

//go:noinline
func main() {
	var a []*int
	var b = 3
	a = append(a, &b)
}

逃逸分析:

 ~/workspace/test  go tool compile -m main.go
main.go:6:6: moved to heap: b

其中 將 b 追加到 a 切片中, 最終 b 發(fā)生了逃逸

四、結(jié)論

golang 中的變量?jī)?nèi)存分配在堆上還是在棧上,是由編譯器做逃逸分析之后決定的。

到此這篇關(guān)于golang內(nèi)存逃逸分析的文章就介紹到這了,更多相關(guān)golang 內(nèi)存逃逸內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • golang 實(shí)現(xiàn)struct、json、map互相轉(zhuǎn)化

    golang 實(shí)現(xiàn)struct、json、map互相轉(zhuǎn)化

    這篇文章主要介紹了golang 實(shí)現(xiàn)struct、json、map互相轉(zhuǎn)化,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • 破解IDEA(Goland)注冊(cè)碼設(shè)置 license server一直有效不過(guò)期的過(guò)程詳解

    破解IDEA(Goland)注冊(cè)碼設(shè)置 license server一直有效不過(guò)期的過(guò)程詳解

    這篇文章主要介紹了破解IDEA(Goland)注冊(cè)碼設(shè)置 license server一直有效不過(guò)期,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • 徹底理解golang中什么是nil

    徹底理解golang中什么是nil

    這篇文章主要介紹了golang中的nil用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • 使用Go實(shí)現(xiàn)健壯的內(nèi)存型緩存的方法

    使用Go實(shí)現(xiàn)健壯的內(nèi)存型緩存的方法

    這篇文章主要介紹了使用Go實(shí)現(xiàn)健壯的內(nèi)存型緩存,本文比較了字節(jié)緩存和結(jié)構(gòu)體緩存的優(yōu)劣勢(shì),介紹了緩存穿透、緩存錯(cuò)誤、緩存預(yù)熱、緩存?zhèn)鬏?、故障轉(zhuǎn)移、緩存淘汰等問(wèn)題,并對(duì)一些常見(jiàn)的緩存庫(kù)進(jìn)行了基準(zhǔn)測(cè)試,需要的朋友可以參考下
    2022-05-05
  • Golang實(shí)現(xiàn)將中文轉(zhuǎn)化為拼音

    Golang實(shí)現(xiàn)將中文轉(zhuǎn)化為拼音

    這篇文章主要為大家詳細(xì)介紹了如何通過(guò)Golang實(shí)現(xiàn)將中文轉(zhuǎn)化為拼音功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-02-02
  • 詳解Golang中使用map時(shí)的注意問(wèn)題

    詳解Golang中使用map時(shí)的注意問(wèn)題

    Golang中的map是一種數(shù)據(jù)結(jié)構(gòu),它允許你使用鍵值對(duì)的形式存儲(chǔ)和訪問(wèn)數(shù)據(jù),map在Go中是非排序的,提供了高效查找、插入和刪除元素的能力,特別是當(dāng)鍵是不可變類(lèi)型,本文給大家詳細(xì)介紹了Golang中使用map時(shí)的注意問(wèn)題,需要的朋友可以參考下
    2024-06-06
  • Golang小數(shù)操作指南之判斷小數(shù)點(diǎn)位數(shù)與四舍五入

    Golang小數(shù)操作指南之判斷小數(shù)點(diǎn)位數(shù)與四舍五入

    這篇文章主要給大家介紹了關(guān)于Golang小數(shù)操作指南之判斷小數(shù)點(diǎn)位數(shù)與四舍五入的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-03-03
  • go語(yǔ)言搬磚之go jmespath實(shí)現(xiàn)查詢(xún)json數(shù)據(jù)

    go語(yǔ)言搬磚之go jmespath實(shí)現(xiàn)查詢(xún)json數(shù)據(jù)

    這篇文章主要為大家介紹了go語(yǔ)言搬磚之go jmespath實(shí)現(xiàn)查詢(xún)json數(shù)據(jù),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Golang請(qǐng)求fasthttp實(shí)踐

    Golang請(qǐng)求fasthttp實(shí)踐

    本文主要介紹了Golang請(qǐng)求fasthttp實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Go?modules?replace解決Go依賴(lài)引用問(wèn)題

    Go?modules?replace解決Go依賴(lài)引用問(wèn)題

    這篇文章主要為大家介紹了Go?modules?replace解決Go依賴(lài)引用問(wèn)題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06

最新評(píng)論