GoLang逃逸分析講解
概念
當一個對象的指針在被多個方法或者線程引用,稱為逃逸分析, 逃逸分析決定一個變量分配在堆上還是棧上, 當然是否發(fā)生逃逸是由編譯器決定的
分配棧和堆上變量的問題
1.局部變量在棧上(靜態(tài)分配),函數執(zhí)行完畢后,自動被棧回收,導致其他對此變量引用出現painc null 指針異常, 棧用戶態(tài)實現goroutine 作為執(zhí)行上下文
2.將變量 new 方式分配在堆上(動態(tài)分配),堆上有個特點,變量不會被刪除,但是會造成內存異常
// 如下代碼導致 程序崩潰, 調用棧獲取危險的懸掛指針 int *foo ( void) { int t = 3; return &t; }
1. 棧上分配內存好處: 一般棧內存 2-4 MB
a. 回收快: 減少GC壓力,當函數返回回收資源。不需要標記清除
b. 分配快棧分配比堆快,不會有內存碎片
c. 并發(fā)快, 清除同步,如果定義對象上有同步鎖,卻只有一個線程訪問,此時逃逸分析機器碼 去掉同步鎖
總結: 逃逸分析目標:盡可能的使用棧分配內存 go build -gcflags ‘-m -N -l’ 方式編譯逃逸分析結果
逃逸分析準則
如果一個函數返回對變量的引用,那么他就發(fā)生逃逸
- 函數外部沒有引用,優(yōu)先分配到棧中(指向棧對象指針不能存在堆中)-- 該指針指向無效值或錯誤的內存值
- 函數外部存在引用,必定分配到堆中(指向棧對象指針不能在棧對象回收后存活-- 指向的內存不合法)
CCN_ProLang/CoreGo/GoreGo 下面有對應的文檔參考
逃逸分析大致思路
1.最重要函數 escape.go
$GOROOT/src/cmd/compile/internal/gc/escape.go
1. 首先構建一個有向無環(huán)圖加權圖,頂點(語句和表達式分配的變量),邊(代表變量之間的賦值關系)
2. 遍歷該有向加權圖,圖中違反上面兩個不變條件的賦值路徑,算法還記錄每個函數的參數到堆的數據流和其返回值的數據流
權重
// p =&q -1 // 最低值
// p =q 0
// p = *q // 解引用 1
// p = **q 2
示例: root =&L , L 節(jié)點的指針指向root, 因此 root有一條邊,src 就是L,該權重就是 -1
3. 逃逸分析: 分析 分配內存地方與使用 是否發(fā)生逃逸
4. go build -gcflags = "-m -m -m -m -W -W -N -l"
1. 當函數中變量返回值, 它將不可能分配在棧上
2.在循環(huán)內被重新賦值的變量大部分場景分配在堆上
3.在閉包外聲明的變量在閉包內賦值失效后,需要分配在堆上
是否發(fā)生逃逸,這一點使用編譯器決定的。導致后果:1. GC頻繁導致CPU壓力大 2.導致性能下降很大
1. 一些逃逸案例: 2. 函數返回變量取地址 導致逃逸 func GetUserInfo(userInfo UserData) *UserData{ // 編譯器判斷外部使用 發(fā)生逃逸 ,傳入的實參對象 取地址類似復制一份 return &userInfo } //修改 將入參修改成指針, 中間沒有新結構體沒有變化 沒有發(fā)生逃逸 func GetUserInfo(userInfo *UserData) *UserData { return userInfo } 案例二:不確定類型逃逸 func MyPrintLn(one interface{}) (n int, err error){ var userInfo = new(User) userInfo.name = one // 泛型賦值逃逸 類型轉換時候發(fā)生逃逸 return } 變量確定具體類型 示例三: 間接變量賦值 閉包 var { UserOne User // 值對象 userTwo = new(User) // 引用對象 } userOne.name = "one" // 不逃逸 userTwo.name = "two" // 逃逸 userOne.age = new(int) // 不逃逸 userTwo.age = new(int) // 逃逸 引用對象在進行引用對象 只能分配堆上 引用對象: 編譯器先分析器userTwo 對象分配到堆上,成員變量name,age 引用類型,保證不出現在棧上 導致對象userTwo 被回收 所有 name,age 需要逃逸 優(yōu)化建議: 不要將引用對象賦值給引用對象
總結
必然不會發(fā)生逃逸的情況:
1. 指針被沒有發(fā)生逃逸的變量引用
2. 僅僅在函數被對變量進行取地址操作,沒有將指針傳出
一定逃逸
構造函數new/make 返回的指針變量一定逃逸
2. 被已經逃逸指針變量引用指針,一定發(fā)生逃逸
3.指針類型是slice,map,chan 引用指針一定發(fā)生逃逸
Maybe 逃逸
將指針作為入參傳給別的函數,這里看指針在被傳入函數的處理過程,如果發(fā)生上邊三種情況會逃逸,否則不會
到此這篇關于GoLang逃逸分析講解的文章就介紹到這了,更多相關Go逃逸內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
GO接收GET/POST參數及發(fā)送GET/POST請求的實例詳解
這篇文章主要介紹了GO接收GET/POST參數及發(fā)送GET/POST請求,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12