关于go:你知道-GO-中什么情况会变量逃逸吗

45次阅读

共计 2094 个字符,预计需要花费 6 分钟才能阅读完成。

你晓得 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

欢送点赞,关注,珍藏

敌人们,你的反对和激励,是我保持分享,提高质量的能源

好了,本次就到这里

技术是凋谢的,咱们的心态,更应是凋谢的。拥抱变动,背阴而生,致力向前行。

我是 阿兵云原生,欢送点赞关注珍藏,下次见~

正文完
 0