成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

go語(yǔ)言?xún)?nèi)存逃逸優(yōu)化 go語(yǔ)言?xún)?nèi)存不斷升高

Go 語(yǔ)言?xún)?nèi)存管理(三):逃逸分析

Go 語(yǔ)言較之 C 語(yǔ)言一個(gè)很大的優(yōu)勢(shì)就是自帶 GC 功能,可 GC 并不是沒(méi)有代價(jià)的。寫(xiě) C 語(yǔ)言的時(shí)候,在一個(gè)函數(shù)內(nèi)聲明的變量,在函數(shù)退出后會(huì)自動(dòng)釋放掉,因?yàn)檫@些變量分配在棧上。如果你期望變量的數(shù)據(jù)可以在函數(shù)退出后仍然能被訪問(wèn),就需要調(diào)用 malloc 方法在堆上申請(qǐng)內(nèi)存,如果程序不再需要這塊內(nèi)存了,再調(diào)用 free 方法釋放掉。Go 語(yǔ)言不需要你主動(dòng)調(diào)用 malloc 來(lái)分配堆空間,編譯器會(huì)自動(dòng)分析,找出需要 malloc 的變量,使用堆內(nèi)存。編譯器的這個(gè)分析過(guò)程就叫做逃逸分析。

創(chuàng)新互聯(lián)公司是一家以成都網(wǎng)站建設(shè)公司、網(wǎng)頁(yè)設(shè)計(jì)、品牌設(shè)計(jì)、軟件運(yùn)維、seo優(yōu)化、小程序App開(kāi)發(fā)等移動(dòng)開(kāi)發(fā)為一體互聯(lián)網(wǎng)公司。已累計(jì)為格柵板等眾行業(yè)中小客戶(hù)提供優(yōu)質(zhì)的互聯(lián)網(wǎng)建站和軟件開(kāi)發(fā)服務(wù)。

所以你在一個(gè)函數(shù)中通過(guò) dict := make(map[string]int) 創(chuàng)建一個(gè) map 變量,其背后的數(shù)據(jù)是放在棧空間上還是堆空間上,是不一定的。這要看編譯器分析的結(jié)果。

可逃逸分析并不是百分百準(zhǔn)確的,它有缺陷。有的時(shí)候你會(huì)發(fā)現(xiàn)有些變量其實(shí)在棧空間上分配完全沒(méi)問(wèn)題的,但編譯后程序還是把這些數(shù)據(jù)放在了堆上。如果你了解 Go 語(yǔ)言編譯器逃逸分析的機(jī)制,在寫(xiě)代碼的時(shí)候就可以有意識(shí)地繞開(kāi)這些缺陷,使你的程序更高效。

Go 語(yǔ)言雖然在內(nèi)存管理方面降低了編程門(mén)檻,即使你不了解堆棧也能正常開(kāi)發(fā),但如果你要在性能上較真的話(huà),還是要掌握這些基礎(chǔ)知識(shí)。

這里不對(duì)堆內(nèi)存和棧內(nèi)存的區(qū)別做太多闡述。簡(jiǎn)單來(lái)說(shuō)就是, 棧分配廉價(jià),堆分配昂貴。 ??臻g會(huì)隨著一個(gè)函數(shù)的結(jié)束自動(dòng)釋放,堆空間需要時(shí)間 GC 模塊不斷地跟蹤掃描回收。如果對(duì)這兩個(gè)概念有些迷糊,建議閱讀下面 2 個(gè)文章:

這里舉一個(gè)小例子,來(lái)對(duì)比下堆棧的差別:

stack 函數(shù)中的變量 i 在函數(shù)退出會(huì)自動(dòng)釋放;而 heap 函數(shù)返回的是對(duì)變量 i 的引用,也就是說(shuō) heap() 退出后,表示變量 i 還要能被訪問(wèn),它會(huì)自動(dòng)被分配到堆空間上。

他們編譯出來(lái)的代碼如下:

邏輯的復(fù)雜度不言而喻,從上面的匯編中可看到, heap() 函數(shù)調(diào)用了 runtime.newobject() 方法,它會(huì)調(diào)用 mallocgc 方法從 mcache 上申請(qǐng)內(nèi)存,申請(qǐng)的內(nèi)部邏輯前面文章已經(jīng)講述過(guò)。堆內(nèi)存分配不僅分配上邏輯比棧空間分配復(fù)雜,它最致命的是會(huì)帶來(lái)很大的管理成本,Go 語(yǔ)言要消耗很多的計(jì)算資源對(duì)其進(jìn)行標(biāo)記回收(也就是 GC 成本)。

Go 編輯器會(huì)自動(dòng)幫我們找出需要進(jìn)行動(dòng)態(tài)分配的變量,它是在編譯時(shí)追蹤一個(gè)變量的生命周期,如果能確認(rèn)一個(gè)數(shù)據(jù)只在函數(shù)空間內(nèi)訪問(wèn),不會(huì)被外部使用,則使用??臻g,否則就要使用堆空間。

我們?cè)? go build 編譯代碼時(shí),可使用 -gcflags '-m' 參數(shù)來(lái)查看逃逸分析日志。

以上面的兩個(gè)函數(shù)為例,編譯的日志輸出是:

日志中的 i escapes to heap 表示該變量數(shù)據(jù)逃逸到了堆上。

需要使用堆空間,所以逃逸,這沒(méi)什么可爭(zhēng)議的。但編譯器有時(shí)會(huì)將 不需要 使用堆空間的變量,也逃逸掉。這里是容易出現(xiàn)性能問(wèn)題的大坑。網(wǎng)上有很多相關(guān)文章,列舉了一些導(dǎo)致逃逸情況,其實(shí)總結(jié)起來(lái)就一句話(huà):

多級(jí)間接賦值容易導(dǎo)致逃逸 。

這里的多級(jí)間接指的是,對(duì)某個(gè)引用類(lèi)對(duì)象中的引用類(lèi)成員進(jìn)行賦值。Go 語(yǔ)言中的引用類(lèi)數(shù)據(jù)類(lèi)型有 func , interface , slice , map , chan , *Type(指針) 。

記住公式 Data.Field = Value ,如果 Data , Field 都是引用類(lèi)的數(shù)據(jù)類(lèi)型,則會(huì)導(dǎo)致 Value 逃逸。這里的等號(hào) = 不單單只賦值,也表示參數(shù)傳遞。

根據(jù)公式,我們假設(shè)一個(gè)變量 data 是以下幾種類(lèi)型,相應(yīng)的可以得出結(jié)論:

下面給出一些實(shí)際的例子:

如果變量值是一個(gè)函數(shù),函數(shù)的參數(shù)又是引用類(lèi)型,則傳遞給它的參數(shù)都會(huì)逃逸。

上例中 te 的類(lèi)型是 func(*int) ,屬于引用類(lèi)型,參數(shù) *int 也是引用類(lèi)型,則調(diào)用 te(j) 形成了為 te 的參數(shù)(成員) *int 賦值的現(xiàn)象,即 te.i = j 會(huì)導(dǎo)致逃逸。代碼中其他幾種調(diào)用都沒(méi)有形成 多級(jí)間接賦值 情況。

同理,如果函數(shù)的參數(shù)類(lèi)型是 slice , map 或 interface{} 都會(huì)導(dǎo)致參數(shù)逃逸。

匿名函數(shù)的調(diào)用也是一樣的,它本質(zhì)上也是一個(gè)函數(shù)變量。有興趣的可以自己測(cè)試一下。

只要使用了 Interface 類(lèi)型(不是 interafce{} ),那么賦值給它的變量一定會(huì)逃逸。因?yàn)? interfaceVariable.Method() 先是間接的定位到它的實(shí)際值,再調(diào)用實(shí)際值的同名方法,執(zhí)行時(shí)實(shí)際值作為參數(shù)傳遞給方法。相當(dāng)于 interfaceVariable.Method.this = realValue

向 channel 中發(fā)送數(shù)據(jù),本質(zhì)上就是為 channel 內(nèi)部的成員賦值,就像給一個(gè) slice 中的某一項(xiàng)賦值一樣。所以 chan *Type , chan map[Type]Type , chan []Type , chan interface{} 類(lèi)型都會(huì)導(dǎo)致發(fā)送到 channel 中的數(shù)據(jù)逃逸。

這本來(lái)也是情理之中的,發(fā)送給 channel 的數(shù)據(jù)是要與其他函數(shù)分享的,為了保證發(fā)送過(guò)去的指針依然可用,只能使用堆分配。

可變參數(shù)如 func(arg ...string) 實(shí)際與 func(arg []string) 是一樣的,會(huì)增加一層訪問(wèn)路徑。這也是 fmt.Sprintf 總是會(huì)使參數(shù)逃逸的原因。

例子非常多,這里不能一一列舉,我們只需要記住分析方法就好,即,2 級(jí)或更多級(jí)的訪問(wèn)賦值會(huì) 容易 導(dǎo)致數(shù)據(jù)逃逸。這里加上 容易 二字是因?yàn)殡S著語(yǔ)言的發(fā)展,相信這些問(wèn)題會(huì)被慢慢解決,但現(xiàn)階段,這個(gè)可以作為我們分析逃逸現(xiàn)象的依據(jù)。

下面代碼中包含 2 種很常規(guī)的寫(xiě)法,但他們卻有著很大的性能差距,建議自己想下為什么。

Benchmark 和 pprof 給出的結(jié)果:

熟悉堆棧概念可以讓我們更容易看透 Go 程序的性能問(wèn)題,并進(jìn)行優(yōu)化。

多級(jí)間接賦值會(huì)導(dǎo)致 Go 編譯器出現(xiàn)不必要的逃逸,在一些情況下可能我們只需要修改一下數(shù)據(jù)結(jié)構(gòu)就會(huì)使性能有大幅提升。這也是很多人不推薦在 Go 中使用指針的原因,因?yàn)樗鼤?huì)增加一級(jí)訪問(wèn)路徑,而 map , slice , interface{} 等類(lèi)型是不可避免要用到的,為了減少不必要的逃逸,只能拿指針開(kāi)刀了。

大多數(shù)情況下,性能優(yōu)化都會(huì)為程序帶來(lái)一定的復(fù)雜度。建議實(shí)際項(xiàng)目中還是怎么方便怎么寫(xiě),功能完成后通過(guò)性能分析找到瓶頸所在,再對(duì)局部進(jìn)行優(yōu)化。

【golang】?jī)?nèi)存逃逸常見(jiàn)情況和避免方式

因?yàn)槿绻兞康膬?nèi)存發(fā)生逃逸,它的生命周期就是不可知的,其會(huì)被分配到堆上,而堆上分配內(nèi)存不能像棧一樣會(huì)自動(dòng)釋放,為了解放程序員雙手,專(zhuān)注于業(yè)務(wù)的實(shí)現(xiàn),go實(shí)現(xiàn)了gc垃圾回收機(jī)制,但gc會(huì)影響程序運(yùn)行性能,所以要盡量減少程序的gc操作。

1、在方法內(nèi)把局部變量指針?lè)祷?,被外部引用,其生命周期大于棧,則溢出。

2、發(fā)送指針或帶有指針的值到channel,因?yàn)榫幾g時(shí)候無(wú)法知道那個(gè)goroutine會(huì)在channel接受數(shù)據(jù),編譯器無(wú)法知道什么時(shí)候釋放。

3、在一個(gè)切片上存儲(chǔ)指針或帶指針的值。比如[]*string,導(dǎo)致切片內(nèi)容逃逸,其引用值一直在堆上。

4、因?yàn)榍衅腶ppend導(dǎo)致超出容量,切片重新分配地址,切片背后的存儲(chǔ)基于運(yùn)行時(shí)的數(shù)據(jù)進(jìn)行擴(kuò)充,就會(huì)在堆上分配。

5、在interface類(lèi)型上調(diào)用方法,在Interface調(diào)用方法是動(dòng)態(tài)調(diào)度的,只有在運(yùn)行時(shí)才知道。

1、go語(yǔ)言的接口類(lèi)型方法調(diào)用是動(dòng)態(tài),因此不能在編譯階段確定,所有類(lèi)型結(jié)構(gòu)轉(zhuǎn)換成接口的過(guò)程會(huì)涉及到內(nèi)存逃逸發(fā)生,在頻次訪問(wèn)較高的函數(shù)盡量調(diào)用接口。

2、不要盲目使用變量指針作為參數(shù),雖然減少了復(fù)制,但變量逃逸的開(kāi)銷(xiāo)更大。

3、預(yù)先設(shè)定好slice長(zhǎng)度,避免頻繁超出容量,重新分配。

什么是逃逸分析?

在C語(yǔ)言中,可以使用malloc和free手動(dòng)在堆上分配和回收內(nèi)存。Go語(yǔ)言中,堆內(nèi)存是通過(guò)垃圾回收機(jī)制自動(dòng)管理的,無(wú)需開(kāi)發(fā)者指定。那么,Go編譯器怎么知道某個(gè)變量需要分配在棧上,還是堆上呢?編譯器決定內(nèi)存分配位置的方式,就稱(chēng)之為逃逸分析(escape analysis)。逃逸分析由編譯器完成,作用于編譯階段。

面試問(wèn)題總結(jié)(一)Golang

使用go語(yǔ)言的好處: go語(yǔ)言的設(shè)計(jì)是務(wù)實(shí)的, go在針對(duì)并發(fā)上進(jìn)行了優(yōu)化, 并且支持大規(guī)模高并發(fā), 又由于單一的碼格式, 相比于其他語(yǔ)言更具有可讀性, 在垃圾回收上比java和Python更有效, 因?yàn)樗呛统绦蛲瑫r(shí)執(zhí)行的.

1. 進(jìn)程, 線程, 協(xié)程的區(qū)別, 協(xié)程的優(yōu)勢(shì)

2. 講一下GMP模型(重點(diǎn))

3. Go的GC, 混合寫(xiě)屏障(重點(diǎn))

4. go的Slice和數(shù)組的區(qū)別, slice的擴(kuò)容原理(重點(diǎn))

5. 講一下channel,實(shí)現(xiàn)原理(重點(diǎn))

6. 講一下Go的Map的實(shí)現(xiàn)原理, 是否線程安全, 如何實(shí)現(xiàn)安全(重點(diǎn))

7. new 和 make 的區(qū)別

8. 說(shuō)一下內(nèi)存逃逸

9. 函數(shù)傳指針和傳值有什么區(qū)別

10. goroutine之間的通信方式

11. 測(cè)試是怎么做的(單元測(cè)試, 壓力測(cè)試)

12. 堆和棧的區(qū)別

網(wǎng)站名稱(chēng):go語(yǔ)言?xún)?nèi)存逃逸優(yōu)化 go語(yǔ)言?xún)?nèi)存不斷升高
URL分享:http://jinyejixie.com/article24/hejice.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供建站公司、品牌網(wǎng)站建設(shè)外貿(mào)網(wǎng)站建設(shè)、標(biāo)簽優(yōu)化、商城網(wǎng)站面包屑導(dǎo)航

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都做網(wǎng)站