golang 中的闭包之函数执行完毕后对携程中使用的闭包变量的影响

看到一个问题感觉很神奇啊,按照以前 Java 或者其他的语言惯例,函数执行完毕后再去引用函数内定义的变量应该会出问题,但是 go 好像不是这个样子,当函数执行完毕退出后,携程中依旧能够正常使用闭包捕获的变量,如:

package main

import (
	"fmt"
)

type mystruct struct {
	str string
}

func main() {
	// 死循环一下
	test()
	for {

	}
}

func test() {
	fmt.Println("test in!")
	testStruct := mystruct{
		str: "xxxxxxxxx",
	}
	fmt.Printf("%p\n", &testStruct)
	go testGrountine(&testStruct)
	fmt.Println("test out!")
}

func testGrountine(p *mystruct) {
	fmt.Println("Grountine in!")
	fmt.Printf("%p\n", p)
	time.Sleep(1)
	fmt.Println(p.str)
	fmt.Println("Grountine quit!")
}


// test in!
// 0xc000088220   地址 1 
// test out!
// Grountine in!
// 0xc000088220   地址 2
// xxxxxxxxx
// Grountine quit!

可以从上方的地址输出中看到并没有发生值的拷贝,但是在函数执行完毕退出后,协程依旧能够使用捕获到的变量。很神奇,这种情况是由于发生了变量逃逸,具体的可以参考这一篇文章 Golang 闭包的底层实现https://zhuanlan.zhihu.com/p/360939266

通过 go build -gcflags '-m -l' 命令分析一下代码可以看到,捕获的变量逃逸到了堆上,所以在函数执行完毕后并没有被释放掉:

# command-line-arguments
.\main.go:30:20: leaking param: p
.\main.go:31:13: ... argument does not escape
.\main.go:31:14: "Grountine in!" escapes to heap
.\main.go:32:12: ... argument does not escape
.\main.go:34:13: ... argument does not escape
.\main.go:34:15: p.str escapes to heap
.\main.go:35:13: ... argument does not escape
.\main.go:35:14: "Grountine quit!" escapes to heap
.\main.go:22:2: moved to heap: testStruct
.\main.go:21:13: ... argument does not escape
.\main.go:21:14: "test in!" escapes to heap
.\main.go:25:12: ... argument does not escape
.\main.go:27:13: ... argument does not escape
.\main.go:27:14: "test out!" escapes to heap
posted @ 2023-04-25 20:31  绯狱丸丶  阅读(24)  评论(0编辑  收藏  举报