Go 的堆和栈
golang的垃圾回收是针对堆的(垃圾回收都是针对堆的,这里只是做一个简单的证明)
引用类型的全局变量内存分配在堆上,值类型的全局变量分配在栈上
堆和栈的简单说明:
1.栈(操作系统):由操作系统自动分配释放
2.堆(操作系统): 一般由程序员分配释放,例如在c/c++中,在golang,java,python有自动的垃圾回收机制
我们都知道变量占有内存,内存在底层分配上有堆和栈。
注意这里说的是"通常",因为变量又分为局部变量和全局变量。
以下是摘录go圣经的一部分:
编译器会自动选择在栈上还是在堆上分配局部变量的存储空间,但可能令人惊讶的是,这个选择并不是由用var还是new声明变量的方式决定的。
var global *int
func f() {
var x int
x = 1
global = &x
}
func g() {
y := new(int)
*y = 1
}
函数里的x变量必须在堆上分配,因为它在函数退出后依然可以通过包一级的global变量找到,虽然它是在函数内部定义的;用Go语言的术语说,这个x局部变量从函数f中逃逸了。相反,当g函数返回时,变量*y将是不可达的,也就是说可以马上被回收的。因此,y并没有从函数g中逃逸,编译器可以选择在栈上分配y的存储空间(译注:也可以选择在堆上分配,然后由Go语言的GC回收这个变量的内存空间),虽然这里用的是new方式。其实在任何时候,你并不需为了编写正确的代码而要考虑变量的逃逸行为,要记住的是,逃逸的变量需要额外分配内存,同时对性能的优化可能会产生细微的影响。
个人理解补充说明:go语言编译器会自动决定把一个变量放在栈还是放在堆,编译器会做逃逸分析(escape analysis),当发现变量的作用域没有跑出函数范围,就可以在栈上,反之则必须分配在堆。
可参考 Golang中的逃逸分析
(以下实验方法说明请参照Go的原生map中删除元素,内存会自动释放吗?)
说明的问题:引用类型的全局变量的内存可以被垃圾回收
实验结果:
实验结论:
引用类型的全局变量的内存被垃圾回收,先不下结论引用类型的全局变量的内存分配在栈上还是堆上
说明的问题:引用类型的局部变量的内存没被垃圾回收
将代码中的a改为局部变量,其他地方完全不懂。为了简洁,就不上代码了
直接看实验结果:
实验结论:
引用类型的局局变量的内存没被垃圾回收,先不下结论引用类型的局局变量的内存分配在栈上还是堆上
做了以上两个实验。只能知道内存是否被垃圾回收,至于回收的是栈上内存还是堆上内存不得而知。原因在于不知道变量内存在栈上还是堆上。
为了解决这一问题,我们按照go圣经所说,将局部变量做一步文中的处理,是它必须在推上分配内存
说明的问题:
实验代码:
实验结果:
实验结论:
由于对局部变量做了处理,使其确定在堆上分配内存,就被垃圾回收了
由此证明,Go语言的GC回收只回收堆上的内存