網站首頁 編程語言 正文
你知道 GO 中什么情況會變量逃逸嗎?首先我們先來看看什么是變量逃逸
Go 語言將這個以前我們寫 C/C++ 時候需要做的內存規劃和分配,全部整合到了 GO 的編譯器中,GO 中將這個稱為 變量逃逸
GO 通過編譯器分析代碼的特征和代碼的生命周期,決定應該使用堆還是棧來進行內存分配
C 代碼 和 GO 代碼對比哪個會崩潰?
咱們寫一個簡單的例子,在 C 里面內存分配到棧上面還是堆上面是一個很明確的事情
例如
函數中的變量是分配在棧上面,會隨著該函數調用完畢后隨之銷毀掉
程序員自己 malloc 開辟的內存是在堆上面,需要程序員自己去釋放
那么問題來了:
如果我們將某一個函數中的局部變量的地址(全篇以局部變量為例),作為該函數的返回值,最終在函數外部去訪問這個局部變量的地址,是否會出錯呢?一起來看看吧
C 程序
test.c
int * get_res(int num){ int tmp = num + 10; return &tmp; } int main(){ int * res = get_res(80); printf("%d -- %p\n" , *res, res); }
上面寫了一個簡單的 C 代碼,獲取傳入數據并 + 10 得到的結果
# gcc test.c -o test test.c: In function ‘get_res': test.c:7:12: warning: function returns address of local variable [-Wreturn-local-addr] return &tmp; ^~~~ # ./test Segmentation fault
這里可以看出編譯程序,報了 warning 了,不過不影響程序的編譯 , 這個 warning 報錯信息是 因為我們返回了臨時變量的地址,C 編譯器檢測到了,給我們拋出了一個 warning
執行編譯的程序后,崩潰了 , 熟悉 C 的小伙伴一點都不驚慌,他們不會寫出這種代碼
出現 段錯誤 的原因很明顯,上面有說到,是因為外部訪問了局部變量的地址,外部訪問的時候,此時這個局部變量已經被銷毀了,此時外部訪問的這個指針,屬于野指針,因此出現程序崩潰
GO 程序
go 程序的邏輯和上面 C 程序的邏輯一模一樣,那么我們看看是否會出現程序崩潰呢
func getRes(num int) *int { tmp := num + 10 return &tmp } func main() { res := getRes(80) fmt.Printf("%d -- %p\n", *res, res) }
執行上述代碼,查看效果
# go run main.go 90 -- 0xc420018078
熟悉 go 語言的 小伙伴看到這里心中也毫無波瀾,程序正常執行,沒有崩潰,因為他們知道原因,這個現象屬于 變量逃逸
那么我們一起來看看 GO 為什么會這樣做,是如何做的呢?
GO 的逃逸是啥樣子的?
上面有說到 GO 不會像 C/C++ 一樣需要程序員自己去關心內存分配,是期望 GO 程序員更多的關注邏輯
因此內存分配這一塊,GO 編譯器都做的妥妥的,一個變量是分配在棧上面還是堆上面,不是簡單的看一個變量是局部變量就分配到棧上,這個是根據具體的使用的,有時候它也會被分配到堆上面
當我們發現本應該分配在棧上面的變量,卻分配在堆上面了,說明發生了逃逸
開始探究和驗證
我們可以嘗試寫一個簡單的 demo ,還是將局部變量的地址返回到外部去,外部來訪問這個局部變量的地址
func getRes(tmp int) *int { var t1 int = 1 var t2 int = 2 var t3 int = 3 println(&tmp, &t1, &t2, &t3) return &t2 } func main() { res := getRes(80) println(*res, res) }
執行上述代碼查看效果
# go run main.go
0xc420045f50 0xc420045f68 0xc420045f60 0xc420045f58
2 0xc420045f60
通過上面的將變量地址打印出來貌似沒有看出上面端倪,地址是也是連續的
那么我們使用 go 提供的工具來看看這個程序是不是存在逃逸
執行 # go tool compile -m main.go
查看效果如下
main.go:11:9: &t2 escapes to heap main.go:6:6: moved to heap: t2
go tool compile
工具很明顯的調試出來說明 t2
這個變量已經逃逸到 堆上面去了
感興趣的話還可以利用工具瞅一眼匯編,多了解一點也有好處
剛才參數 -m 是直接查看是否逃逸,我們可以加 -S 會打印出具體的會變代碼,查看該變量是否是 new 出來的
# go tool compile -S main.go | grep new 0x0035 00053 (main.go:6) CALL runtime.newobject(SB) rel 54+4 t=8 runtime.newobject+0
對應的看看代碼,就是 創建 t2 變量的這一行
對于 go tool compile 工具,我們可以通過 help 命令來查看一下
# go tool compile --help
原文鏈接:https://blog.csdn.net/m0_37322399/article/details/128960879
相關推薦
- 2022-05-09 Python使用Plotly繪制常見5種動態交互式圖表_python
- 2023-12-06 Warn: Could not find @TableId
- 2022-06-23 muduo源碼分析之TcpServer模塊詳細介紹_Redis
- 2023-04-24 一文帶你深入了解C++中音頻PCM數據_C 語言
- 2022-08-11 python中的多線程鎖lock=threading.Lock()使用方式_python
- 2022-06-28 ASP.NET一次性對GridView批量更新多行數據_實用技巧
- 2024-02-28 UNI-APP,text、rich-text控件顯示字符串,當字符串過長時,實現自動換行
- 2022-02-22 Oracle10G序列名因標識符長度太大導致無法創建
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支