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