变量的生命周期指的是在程序运行期间变量有效存在的时间间隔。对于在包一级声明的变量来说,它们的生命周期和整个程序的运行周期是一致的。而相比之下,局部变量的声明周期则是动态的:每次从创建一个新变量的声明语句开始,直到该变量不再被引用为止,然后变量的存储空间可能被回收。函数的参数变量和返回值变量都是局部变量。它们在函数每次被调用的时候创建。
在每次循环的开始会创建临时变量t,然后在每次循环迭代中创建临时变量x和y。临时变量x、y存放在栈中,随着函数执行结束(执行遇到最后一个“}” ),释放其内存。
一般而言,
- 堆(heap):堆是用于存放进程执行中被动态分配的内存段。它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时。新分配的内存就被动态加入到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
- 栈(stack):栈又称堆栈, 是用户存放程序暂时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包含static声明的变量。static意味着在数据段中存放变量)。
编译器会自动选择在栈上还是在堆上分配局部变量的存储空间,但可能令人惊讶的是,这个选择并不是由用var还是new声明变量的方式决定的。
*y*y*y
Go语言的自动垃圾收集器对编写正确的代码是一个巨大的帮助,但也并不是说你完全不用考虑内存了。你虽然不需要显式地分配和释放内存,但是要编写高效的程序你依然需要了解变量的生命周期。例如,如果将指向短生命周期对象的指针保存到具有长生命周期的对象中,特别是保存到全局变量时,会阻止对短生命周期对象的垃圾回收(从而可能影响程序的性能)。
有了以上知识,来看看闭包:
所谓的闭包是指有权访问另一个函数作用域中的变量的函数,就是在一个函数内部创建另一个函数。在Go语言里,所有的匿名函数(Go语言规范中称之为函数字面量)都是闭包。
下面我们通过一个案例,看一下关于闭包的应用。
思考:以下程序执行的结果是
虽然Test( )函数调用了三次,但是输出都是1.原因是:
每次调用Test( )函数,都是重新声明变量x,当函数执行完成后,x会自动被释放所占资源。Test( )保存在栈中,函数结束,栈释放内存。
如果想实现累加运算,这里就需要用到闭包(匿名函数)。
由于在定义Test( )函数时指定了返回的类型是一个匿名函数,并且该匿名函数返回的类型是整型。所以在Test( )函数中定义了一个匿名函数,并且将整个匿名函数返回,匿名函数返回的是整型。在main( )函数中定义了一个变量f,该变量的类型是匿名函数,f( )表示调用执行匿名函数。最终执行完成后发现,实现了数字的累加。虽然Test()已经返回了,但是返回的值:func()还在全局变量中使用,三次调用 f(),因此返回值会保存在堆上,即使栈释放了内存资源,但func()保存在堆中,数据不会释放。
因为匿名函数(闭包),有一个很重要的特点:
它不关心这些捕获了的变量和常量是否已经超出了作用域,所以只有闭包还在使用它,这些变量就还会存在。