目录

为什么栈上内存分配效率高

栈为什么这么快

栈寄存器的支持

CPU有专门的栈寄存器 ESP栈顶寄存器,EBP栈基地址寄存器,对于栈内存的访问CPU可以直接根据栈寄存器保存的地址获取到数据

但是堆内存的访问需要先获取到指令然后从指令中获取到堆内存地址保存在寄存器中,最后再通过寄存器的地址进行寻址

栈空间内存分配和释放比较快

栈空间在编译时就分配内存给变量了,而堆空间需要程序允许时动态的进行系统调用来分配内存。并且栈在分配内存和释放内存只需要移动栈寄存器的指针即可,所以栈的内存分配和释放都是比较快的

栈空间的管理不需要程序员手动管理,而堆空间需要用户程序手动管理,包括一些GC算法等都是针对堆空间的

栈适合函数调用场景

函数调用场景时候用栈这种数据结构,比如

  • 函数调用结束之后,函数里的局部变量都没用了,则此时应该立即释放其内存
  • 函数嵌套调用

栈的缺点

  • 栈因为结构简单,所以无法应对一些需要动态分配的场景
  • 栈空间需要类型大小是固定,缺乏灵活性

Go的内存逃逸

Go使用new make 等创建的对象不一定都是分配在堆上的,这个和其他语言有点不一样

编译器在编译的时候会进行逃逸分析,可能会将函数内的一些局部变量分配到堆上,逃逸分析的命令如下

go build -gcflags '-m -l' main.go #逃逸分析

函数返回指针,编译器会把变量分配到堆上

type User struct {
	Name string
}

func hello() *User {
	u := User{Name: "lyer"}
	return &u
}

new一个对象,make一个对象并不一定会分配到堆上

type User struct {
	Name string
}

func hello() {
	u := new(User)
	u.Name = "lyer"
	c := make([]int, 10)
	c[0] = 1
}

下面的代码也不会逃逸,传入的地址是栈上的地址

func hello() {
	u := new(User)
	u.Name = "lyer"
	sayUser(u)
}

func sayUser(u *User) {
}

对于一个interface等不确定类型的变量会分配到堆上

栈空间不足也会分配到堆上

参考

我要在栈上。不,你应该在堆上

内存逃逸分析