【Go語言基礎2.1】變量逃逸

??編譯器會優先將局部變量存放在棧中,便于及時清理,但是如果存放在棧上的變量被清理了,但是函數其他地方還存在對它的引用,這個時候就會發生不可知的錯誤。例如下面這段程序:

#Include<stdio.h>
char *returnStr() {
    char p[] = "hello world";
    return p;
}

int main() {
    char *str;
    str = returnStr();
    printf("%s\n", str);
    return 0;
}

??由于"hello world"屬于局部變量,會被存儲在棧上,在returnStr函數退出之后,這個函數所占用的棧空間會被清空,這個局部變量也就不會存在,這時main函數再去獲取這塊內存存放的值就會發生異常。這種情況通常稱為變量逃逸
??類似于上面的例子,一個對象的指針被其作用域之外的地方引用,我們就稱這個變量發生了逃逸。
??這種情況對于使用C/C++的情況是經常發生的,然而Go語言對這種情況做了特殊處理,使得編程人員無需為這種事情過度擔心。go語言通過編譯器的逃逸分析,在執行靜態代碼分析時,對內存管理進行優化,將變量分配到合理的位置。

2、逃逸分析

觀察下面這個例子:

package main

import "fmt"

func returnStr() (*string, *string) {
    strStack := "hello world!"
    strHeap := "hello world!"
    strStack2 := "hello world!"
    fmt.Println("strStack is: ", strStack, "strHeap is: ", strHeap, "strStack2 is: ", strStack2)
    return &strHeap, &strStack2
}

func main() {
    str,  _ := returnStr()
    fmt.Println(*str)
}

通過分析可以發現:
1、strStack這個變量在函數returnStr內部使用完成后,就沒用了,這時可以把這個變量分配到棧上。
2、strStack2的地址被返回了,所以也會分配到堆上(這個地方和參考文章說的不同,經過多次實驗,我覺得它還是會被分配到堆上)。
3、對于strHeap這個變量,在函數內部使用完之后,指針又被返回給main函數使用,那就把這個變量分配到堆上。

??如果將變量分配到堆上,可能會造成一定程序的性能損耗,因為不同于棧的自動釋放,分配在堆上的變量,需要Go頻繁地進行垃圾回收。通過逃逸分析,可以將變量在堆和棧上合理地進行分配,避免不必要的性能浪費。
??簡單來說,編譯器通過逃逸分析,分析出變量是否存在外部引用,判斷變量存放的位置

  • 如果外部沒有引用,優先存放到棧中
  • 如果外部存在應用,必然存放到堆中


    逃逸分析

參考文章:
Go變量逃逸分析

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。