首页 > 技术文章 > Go语言之GO 语言变量的生命周期

heych 2020-03-26 21:28 原文

GO 语言变量的生命周期

生命周期是指程序执行过程中变量存在的时间段。

包变量(全局变量)和局部变量两种变量的生命周期。

① 包变量一直常驻在内存到程序的结束,然后被系统垃圾回收器回收。也就是说包变量的生命周期是整个程序的执行时间。
  ② 局部变量,例如一个函数中定义的变量。它有一个动态的生命周期:每次执行生命语句时创建一个新的实体,变量一直生存到它变得不可访问(例如没有外部指针指向它,函数退出我们没有路径能访问到这个变量),这时它占用的存储空间就会被回收。

var p *int    //全局指针变量
func f(){
    var i int
    i = 1
    p = &x    //全局指针变量指向局部变量i
}

func f(){
    p := new(int) //局部指针变量,使用new申请的空间
    *p = 1
}

i是通过var申请的局部变量,但是由于有外部指针指向访问,我们有路径可找到这个空间(变量能够逃逸出函数),所以局部变量i是申请在堆空间上。而第二个程序中p指针变量虽然是使用new申请的空间,但是由于退出函数就没有路径可寻找到它(变量无法逃出函数),所以局部变量p是申请在栈空间上的。 

f函数里的x变量必须在堆上分配,因为它在函数退出后依然可以通过包一级的global变量找到,虽然它是在函数内部定义的;用Go语言的术语说,这个x局部变量从函数f中逃逸了。相反,当g函数返回时,变量*y将是不可达的,也就是说可以马上被回收的。因此,*y并没有从函数g中逃逸,编译器可以选择在栈上分配*y的存储空间(译注:也可以选择在堆上分配,然后由Go语言的GC回收这个变量的内存空间),虽然这里用的是new方式。其实在任何时候,你并不需为了编写正确的代码而要考虑变量的逃逸行为,要记住的是,逃逸的变量需要额外分配内存,同时对性能的优化可能会产生细微的影响。

分析闭包:

由于在定义Test( )函数时指定了返回的类型是一个匿名函数,并且该匿名函数返回的类型是整型。所以在Test( )函数中定义了一个匿名函数,并且将整个匿名函数返回,匿名函数返回的是整型。在main( )函数中定义了一个变量f,该变量的类型是匿名函数,f( )表示调用执行匿名函数。最终执行完成后发现,实现了数字的累加。虽然Test()已经返回了,但是返回的值:func()还在全局变量中使用,三次调用 f(),因此返回值会保存在堆上,即使栈释放了内存资源,但func()保存在堆中,数据不会释放。

因为匿名函数(闭包),有一个很重要的特点:

它不关心这些捕获了的变量和常量是否已经超出了作用域,所以只有闭包还在使用它,这些变量就还会存在。

推荐阅读