Go的变量到底是分配在堆还是栈,我们先聊下下概念。
- 堆(heap) 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
- 栈 (stack) 由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。Go在分配栈的时候,预先分配2k的空间。
理论上分配在栈上的数据操作速度快与堆上。堆上使用的内存需要垃圾回收(GC),栈上的内存是自动释放的且是明确不共享的。
先看个例子 escape.go
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package main
type user struct { name string email string }
func main() {
newUser() }
func newUser() user { u := user{ name: "tom", email: "[email protected]", } return u }
|
编译分析
1 2
| ~ go tool compile -m escape.go escape.go:8:6: can inline main
|
这里只说明main可以内联,此时u是分配到栈上的,因为没有其他程序共享u
变量。继续修改下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package main
type user struct { name string email string }
func main() {
newUser() }
func newUser() *user { u := user{ name: "tom", email: "[email protected]", } return &u }
|
结果
1 2 3 4
| ~ go tool compile -m escape.go escape.go:8:6: can inline main escape.go:18:6: can inline changeName escape.go:18:17: moved to heap: u
|
我们返回user的指针,Go判断这段内存需要被共享于是本身u
在堆上的被移动到了栈了。如果我们修改
1 2 3 4 5 6 7
| func newUser() *user { u := &user{ name: "tom", email: "[email protected]", } return u }
|
1 2 3
| ~ go tool compile -m escape.go escape.go:8:6: can inline main escape.go:17:10: &user literal escapes to heap
|
Go直接将这段内存分配到堆上了。一旦分配在堆上就要进行垃圾回收处理了。这里要注意的每个Goroutine都有自己的栈内存互不影响,Goroutine之间栈内存能不能共享!