前言

最近看了 Understanding Allocations: the Stack and the Heap - GopherCon SG 2019 关于 Golang 内存分配的技术分享, 其对内存的分配机制讲解的很透彻,感觉很棒!本文记录📝学习过程的理解,素材均来自原视频。让我们开始吧!


我们知道程序内存分配有两类区域:栈(Stack)和堆(Heap)。如下图所示,栈是绑定在Goroutine上。堆上一般为共享的变量,由GC(垃圾回收期)管理。

golang memories

为啥要研究内存的分配方式?栈或堆

  • 与程序的正确性无关
  • 确实会影响程序的性能
  • 堆上所有东西都是由GC管理
  • GC很好,但是会导致延迟
    • 这是对于整个程序,而不是创造垃圾的部分

简单来说就是变量分配到栈上,不需要垃圾回收,函数执行完内存自动回收。而当变量分配到堆上时,就需要GC来回收,GC回收是需要成本的。

你需要知道什么?

优化程序的正确性是第一位的,而不是性能。

栈代码演示

stack

square函数返回的类型为值,而返回值会在main方法中进行复制。变量分配在栈上。

栈共享指针

stack share pointer

向下分享来通常分配在栈上(指向函数传指针或引用)

哪一种会分配在栈上

stack 3

向上分享通常分配在堆上(指函数返回值为指针或引用)

逃逸到堆上示例

func main() {
	n := answer()
	println(*n / 2)
}

func answer() *int {
	x := 42
	return &x
}
go build -gcflags '-m -l' main.go
# command-line-arguments
./main.go:32:2: moved to heap: x

什么情况下分配到堆上

  • 当值在函数构造之后可能会被引用时
  • 当编译器确定一个值太大而无法放入栈时
  • 当编译器在编译时不知道值的大小时

哪些类型的值会分配到堆上

  • 与指针共享的值
  • 存储在接口变量中的变量
  • 函数字面量变量
  • 基于 Maps, Channels, Slices 和 Strings

需要记住的点

  • 优化正确性,而不是性能
  • Go 只有在函数返回后能证明一个变量没有被使用的情况下才会将函数变量放入栈中
  • 向下分享来通常分配在栈上(指向函数传指针或引用)
  • 向上分享通常分配在堆上(指函数返回值为指针或引用)
  • 问编译器找出分配方式
  • 不要猜,使用工具