先大概解释下闭包的实现原理。
类是有行为的数据,闭包是有数据的行为。
闭包在go asm层面,会被转换为一个匿名结构体,结构体里面会存储闭包访问到的所有的上下文变量,并且必要的时候以地址的方式(call by name)进行存储。
例如代码:
可以看到14行被转成了一个匿名的结构体,F就是闭包函数的入口地址, 并且存储了参数i的地址,局部变量t222和t111的value。
funcClosure会返回匿名结构体的变量F,也就是闭包函数的入口地址。然后在main里面进行调用。
那么针对value传递进去的变量,lifecycle可以认为是跟原来的变量分离了,交给匿名结构体的实例托管。
对于以地址传递进去的变量,我们再看下具体的funcClosure的asm:
可以对于i1111这个变量,在funcClosure里面新建了一个堆上的变量,并且在闭包里面存储了这个变量的地址。
所以,
“疑问一:使用到外部的变量也是外部函数的局部变量, 虽然变量的生命周期会由gc机制管理, 但是函数(包括匿名函数)的生命周期是整个程序,也就是一直到程序结束,函数都有可能被调用, 那么gc怎么去判定这个变量已经没有用了可以被回收“
上下文局部变量,如果是常量,那么就会被常量替换,这个时候就跟原来的变量无关了,如果是编译期无法确定的变量,在堆上分配空间addr,并将变量的地址存在addr上,addr的生命周期跟闭包声明周期一致。
“疑问二:所有函数类型变量的值相等,指向同一块代码, 那么通过不同的函数类型变量调用闭包函数, 函数怎么确定该函数类型变量对应使用的外部变量是哪个?“
无法确定。因为使用的是同一个闭包实例。
参考: