網(wǎng)站首頁 編程語言 正文
你知道 GO 中什么情況會變量逃逸嗎?首先我們先來看看什么是變量逃逸
Go 語言將這個以前我們寫 C/C++ 時候需要做的內(nèi)存規(guī)劃和分配,全部整合到了 GO 的編譯器中,GO 中將這個稱為 變量逃逸
GO 通過編譯器分析代碼的特征和代碼的生命周期,決定應(yīng)該使用堆還是棧來進行內(nèi)存分配
C 代碼 和 GO 代碼對比哪個會崩潰?
咱們寫一個簡單的例子,在 C 里面內(nèi)存分配到棧上面還是堆上面是一個很明確的事情
例如
函數(shù)中的變量是分配在棧上面,會隨著該函數(shù)調(diào)用完畢后隨之銷毀掉
程序員自己 malloc 開辟的內(nèi)存是在堆上面,需要程序員自己去釋放
那么問題來了:
如果我們將某一個函數(shù)中的局部變量的地址(全篇以局部變量為例),作為該函數(shù)的返回值,最終在函數(shù)外部去訪問這個局部變量的地址,是否會出錯呢?一起來看看吧
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 代碼,獲取傳入數(shù)據(jù)并 + 10 得到的結(jié)果
# 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
執(zhí)行編譯的程序后,崩潰了 , 熟悉 C 的小伙伴一點都不驚慌,他們不會寫出這種代碼
出現(xiàn) 段錯誤 的原因很明顯,上面有說到,是因為外部訪問了局部變量的地址,外部訪問的時候,此時這個局部變量已經(jīng)被銷毀了,此時外部訪問的這個指針,屬于野指針,因此出現(xiàn)程序崩潰
GO 程序
go 程序的邏輯和上面 C 程序的邏輯一模一樣,那么我們看看是否會出現(xiàn)程序崩潰呢
func getRes(num int) *int { tmp := num + 10 return &tmp } func main() { res := getRes(80) fmt.Printf("%d -- %p\n", *res, res) }
執(zhí)行上述代碼,查看效果
# go run main.go 90 -- 0xc420018078
熟悉 go 語言的 小伙伴看到這里心中也毫無波瀾,程序正常執(zhí)行,沒有崩潰,因為他們知道原因,這個現(xiàn)象屬于 變量逃逸
那么我們一起來看看 GO 為什么會這樣做,是如何做的呢?
GO 的逃逸是啥樣子的?
上面有說到 GO 不會像 C/C++ 一樣需要程序員自己去關(guān)心內(nèi)存分配,是期望 GO 程序員更多的關(guān)注邏輯
因此內(nèi)存分配這一塊,GO 編譯器都做的妥妥的,一個變量是分配在棧上面還是堆上面,不是簡單的看一個變量是局部變量就分配到棧上,這個是根據(jù)具體的使用的,有時候它也會被分配到堆上面
當我們發(fā)現(xiàn)本應(yīng)該分配在棧上面的變量,卻分配在堆上面了,說明發(fā)生了逃逸
開始探究和驗證
我們可以嘗試寫一個簡單的 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) }
執(zhí)行上述代碼查看效果
# go run main.go
0xc420045f50 0xc420045f68 0xc420045f60 0xc420045f58
2 0xc420045f60
通過上面的將變量地址打印出來貌似沒有看出上面端倪,地址是也是連續(xù)的
那么我們使用 go 提供的工具來看看這個程序是不是存在逃逸
執(zhí)行 # 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
工具很明顯的調(diào)試出來說明 t2
這個變量已經(jīng)逃逸到 堆上面去了
感興趣的話還可以利用工具瞅一眼匯編,多了解一點也有好處
剛才參數(shù) -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
對應(yīng)的看看代碼,就是 創(chuàng)建 t2 變量的這一行
對于 go tool compile 工具,我們可以通過 help 命令來查看一下
# go tool compile --help
原文鏈接:https://blog.csdn.net/m0_37322399/article/details/128960879
相關(guān)推薦
- 2022-04-12 C語言三分鐘精通時間復(fù)雜度與空間復(fù)雜度_C 語言
- 2022-08-28 ubuntu安裝samba文件共享
- 2022-06-29 python人工智能tensorflow函數(shù)tf.nn.dropout使用方法_python
- 2022-03-17 淺析.netcore中的Configuration具體使用_實用技巧
- 2022-07-18 C語言枚舉類型
- 2024-03-05 git創(chuàng)建分支
- 2023-05-20 linux?shell輸出換行簡單實例_linux shell
- 2022-03-17 .net?程序通過?crontab?無法啟動手動執(zhí)行腳本啟動的方法_實用技巧
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支