Go 語言較之 C 語言一個很大的優(yōu)勢就是自帶 GC 功能,可 GC 并不是沒有代價的。寫 C 語言的時候,在一個函數(shù)內(nèi)聲明的變量,在函數(shù)退出后會自動釋放掉,因為這些變量分配在棧上。如果你期望變量的數(shù)據(jù)可以在函數(shù)退出后仍然能被訪問,就需要調(diào)用 malloc 方法在堆上申請內(nèi)存,如果程序不再需要這塊內(nèi)存了,再調(diào)用 free 方法釋放掉。Go 語言不需要你主動調(diào)用 malloc 來分配堆空間,編譯器會自動分析,找出需要 malloc 的變量,使用堆內(nèi)存。編譯器的這個分析過程就叫做逃逸分析。
創(chuàng)新互聯(lián)專注于企業(yè)全網(wǎng)整合營銷推廣、網(wǎng)站重做改版、清澗網(wǎng)站定制設(shè)計、自適應(yīng)品牌網(wǎng)站建設(shè)、成都h5網(wǎng)站建設(shè)、購物商城網(wǎng)站建設(shè)、集團公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計等建站業(yè)務(wù),價格優(yōu)惠性價比高,為清澗等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
所以你在一個函數(shù)中通過 dict := make(map[string]int) 創(chuàng)建一個 map 變量,其背后的數(shù)據(jù)是放在棧空間上還是堆空間上,是不一定的。這要看編譯器分析的結(jié)果。
可逃逸分析并不是百分百準確的,它有缺陷。有的時候你會發(fā)現(xiàn)有些變量其實在??臻g上分配完全沒問題的,但編譯后程序還是把這些數(shù)據(jù)放在了堆上。如果你了解 Go 語言編譯器逃逸分析的機制,在寫代碼的時候就可以有意識地繞開這些缺陷,使你的程序更高效。
Go 語言雖然在內(nèi)存管理方面降低了編程門檻,即使你不了解堆棧也能正常開發(fā),但如果你要在性能上較真的話,還是要掌握這些基礎(chǔ)知識。
這里不對堆內(nèi)存和棧內(nèi)存的區(qū)別做太多闡述。簡單來說就是, 棧分配廉價,堆分配昂貴。 ??臻g會隨著一個函數(shù)的結(jié)束自動釋放,堆空間需要時間 GC 模塊不斷地跟蹤掃描回收。如果對這兩個概念有些迷糊,建議閱讀下面 2 個文章:
這里舉一個小例子,來對比下堆棧的差別:
stack 函數(shù)中的變量 i 在函數(shù)退出會自動釋放;而 heap 函數(shù)返回的是對變量 i 的引用,也就是說 heap() 退出后,表示變量 i 還要能被訪問,它會自動被分配到堆空間上。
他們編譯出來的代碼如下:
邏輯的復雜度不言而喻,從上面的匯編中可看到, heap() 函數(shù)調(diào)用了 runtime.newobject() 方法,它會調(diào)用 mallocgc 方法從 mcache 上申請內(nèi)存,申請的內(nèi)部邏輯前面文章已經(jīng)講述過。堆內(nèi)存分配不僅分配上邏輯比??臻g分配復雜,它最致命的是會帶來很大的管理成本,Go 語言要消耗很多的計算資源對其進行標記回收(也就是 GC 成本)。
Go 編輯器會自動幫我們找出需要進行動態(tài)分配的變量,它是在編譯時追蹤一個變量的生命周期,如果能確認一個數(shù)據(jù)只在函數(shù)空間內(nèi)訪問,不會被外部使用,則使用棧空間,否則就要使用堆空間。
我們在 go build 編譯代碼時,可使用 -gcflags '-m' 參數(shù)來查看逃逸分析日志。
以上面的兩個函數(shù)為例,編譯的日志輸出是:
日志中的 i escapes to heap 表示該變量數(shù)據(jù)逃逸到了堆上。
需要使用堆空間,所以逃逸,這沒什么可爭議的。但編譯器有時會將 不需要 使用堆空間的變量,也逃逸掉。這里是容易出現(xiàn)性能問題的大坑。網(wǎng)上有很多相關(guān)文章,列舉了一些導致逃逸情況,其實總結(jié)起來就一句話:
多級間接賦值容易導致逃逸 。
這里的多級間接指的是,對某個引用類對象中的引用類成員進行賦值。Go 語言中的引用類數(shù)據(jù)類型有 func , interface , slice , map , chan , *Type(指針) 。
記住公式 Data.Field = Value ,如果 Data , Field 都是引用類的數(shù)據(jù)類型,則會導致 Value 逃逸。這里的等號 = 不單單只賦值,也表示參數(shù)傳遞。
根據(jù)公式,我們假設(shè)一個變量 data 是以下幾種類型,相應(yīng)的可以得出結(jié)論:
下面給出一些實際的例子:
如果變量值是一個函數(shù),函數(shù)的參數(shù)又是引用類型,則傳遞給它的參數(shù)都會逃逸。
上例中 te 的類型是 func(*int) ,屬于引用類型,參數(shù) *int 也是引用類型,則調(diào)用 te(j) 形成了為 te 的參數(shù)(成員) *int 賦值的現(xiàn)象,即 te.i = j 會導致逃逸。代碼中其他幾種調(diào)用都沒有形成 多級間接賦值 情況。
同理,如果函數(shù)的參數(shù)類型是 slice , map 或 interface{} 都會導致參數(shù)逃逸。
匿名函數(shù)的調(diào)用也是一樣的,它本質(zhì)上也是一個函數(shù)變量。有興趣的可以自己測試一下。
只要使用了 Interface 類型(不是 interafce{} ),那么賦值給它的變量一定會逃逸。因為 interfaceVariable.Method() 先是間接的定位到它的實際值,再調(diào)用實際值的同名方法,執(zhí)行時實際值作為參數(shù)傳遞給方法。相當于 interfaceVariable.Method.this = realValue
向 channel 中發(fā)送數(shù)據(jù),本質(zhì)上就是為 channel 內(nèi)部的成員賦值,就像給一個 slice 中的某一項賦值一樣。所以 chan *Type , chan map[Type]Type , chan []Type , chan interface{} 類型都會導致發(fā)送到 channel 中的數(shù)據(jù)逃逸。
這本來也是情理之中的,發(fā)送給 channel 的數(shù)據(jù)是要與其他函數(shù)分享的,為了保證發(fā)送過去的指針依然可用,只能使用堆分配。
可變參數(shù)如 func(arg ...string) 實際與 func(arg []string) 是一樣的,會增加一層訪問路徑。這也是 fmt.Sprintf 總是會使參數(shù)逃逸的原因。
例子非常多,這里不能一一列舉,我們只需要記住分析方法就好,即,2 級或更多級的訪問賦值會 容易 導致數(shù)據(jù)逃逸。這里加上 容易 二字是因為隨著語言的發(fā)展,相信這些問題會被慢慢解決,但現(xiàn)階段,這個可以作為我們分析逃逸現(xiàn)象的依據(jù)。
下面代碼中包含 2 種很常規(guī)的寫法,但他們卻有著很大的性能差距,建議自己想下為什么。
Benchmark 和 pprof 給出的結(jié)果:
熟悉堆棧概念可以讓我們更容易看透 Go 程序的性能問題,并進行優(yōu)化。
多級間接賦值會導致 Go 編譯器出現(xiàn)不必要的逃逸,在一些情況下可能我們只需要修改一下數(shù)據(jù)結(jié)構(gòu)就會使性能有大幅提升。這也是很多人不推薦在 Go 中使用指針的原因,因為它會增加一級訪問路徑,而 map , slice , interface{} 等類型是不可避免要用到的,為了減少不必要的逃逸,只能拿指針開刀了。
大多數(shù)情況下,性能優(yōu)化都會為程序帶來一定的復雜度。建議實際項目中還是怎么方便怎么寫,功能完成后通過性能分析找到瓶頸所在,再對局部進行優(yōu)化。
項目更目錄下執(zhí)行 go tool pprof ,結(jié)束之后會默認進入 PProf 的命令行交互模式,接著輸入 top 10 ,如下圖,
項目根目錄下執(zhí)行 go tool pprof ,結(jié)束之后會默認進入 PProf 的命令行交互模式,接著輸入 top ,如圖所示,
上面可以看到, main.main.fun1 的 cum 大小正好等于自身的 flat 大小加上 main.Add 大小的 flat 大小
應(yīng)用程序發(fā)生異常 未知的軟件異常
1.病毒木馬造成的,在當今互聯(lián)網(wǎng)時代,病毒坐著為了獲得更多的牟利,常用病毒綁架應(yīng)用程序和系統(tǒng)文件,然后某些安全殺毒軟件把被病毒木馬感染的應(yīng)用程序和系統(tǒng)文件當病毒殺了導致的。
2.應(yīng)用程序組件丟失,應(yīng)用程序完整的運行需要一些系統(tǒng)文件或者某些ll文件支持的,如果應(yīng)用程序組件不完整也會導致的。
3.系統(tǒng)文件損壞或丟失,盜版系統(tǒng)或Ghost版本系統(tǒng),很容易出現(xiàn)該問題。
4.操作系統(tǒng)自身的問題,操作系統(tǒng)本身也會有bug 。
5.硬件問題,例如內(nèi)存條壞了或者存在質(zhì)量問題,或者內(nèi)存條的金手指的灰塵特別多。
應(yīng)用程序發(fā)生異常怎么辦
1.檢查電腦是否存在病毒,請使用百度衛(wèi)士進行木馬查殺。
2.系統(tǒng)文件損壞或丟失,盜版系統(tǒng)或Ghost版本系統(tǒng),很容易出現(xiàn)該問題。建議:使用完整版或正版系統(tǒng)。
3.安裝的軟件與系統(tǒng)或其它軟件發(fā)生沖突,找到發(fā)生沖突的軟件,卸載它。如果更新下載補丁不是該軟件的錯誤補丁,也會引起軟件異常,解決辦法:卸載該軟件,重新下載重新安裝試試。順便檢查開機啟動項,把沒必要啟動的啟動項禁止開機啟動。
4.如果檢查上面的都沒問題,可以試試下面的方法。
打開開始菜單→運行→輸入cmd→回車,在命令提示符下輸入下面命令 for %1 in (%windir%\system32\*.dll) do regsvr32.exe /s %1回車。
完成后,在輸入下面
for %i in (%windir%\system32\*.ocx) do regsvr32.exe /s %i 回車。
如果怕輸入錯誤,可以復制這兩條指令,然后在命令提示符后擊鼠標右鍵,打“粘貼”,回車,耐心等待,直到屏幕滾動停止為止。(重啟電腦)。
本文題目:go語言內(nèi)存溢出 go語言內(nèi)存逃逸
文章地址:http://www.rwnh.cn/article8/dosipop.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供面包屑導航、網(wǎng)站營銷、網(wǎng)站制作、用戶體驗、服務(wù)器托管、ChatGPT
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)