Golang 栈内存与堆内存
OS层面
栈内存与堆内存的区别
- 栈内存一般由操作系统分配与释放;堆内存一般由程序自身申请与释放
- 栈内存一般存放函数参数、函数返回值、局部变量、函数调用时的临时上下文等;堆内存一般存放全局变量
- 栈内存比堆内存访问速度更快
- 每个线程分配一个栈内存;每个进程分配一个堆内存
- 栈内存创建时,内存大小是固定的,越界则会发生stack overflow错误;堆内存创建时,内存大小不固定,可随程序运行增加或减少
- 栈内存是由高地址向低地址增长;堆内存是由低地址向高地址增长
Golang内存管理
基础信息
- Golang是自己管理内存,不依赖操作系统,即向操作系统申请一块较大内存,然后自己决定将变量分配到栈空间或对空间
- 分配选择:基本同上面的分配原则,但对于函数的引用参数会有一些特殊。如果编译器无法证明函数返回之后变量是否仍然被引用,此时就必须在堆空间分配该变量,随后采用垃圾回收机制管理,而从避免指针悬空。此外,如果局部变量过大,也会选择分配在堆空间
- 总结:最终的分配空间在于编译器的选择,编译器分析变量的生存周期的过程就叫做逃逸分析
栈内存
- 栈内存的分配与释放全权由操作系统决定,开发者无法控制。一般栈内存会自动创建,函数返回的时候内存会被自动释放。栈内存的分配与释放速度较快
堆内存
- 对存有由于不确定大小,因此代价就是分配速度较慢且会形成内存碎片。堆内存不能自动被编译器释放,只能通过垃圾回收器才能释放
内存逃逸
- 把函数内的局部变量通过指针形式返回
- 发送指针或带有指针的值到channel中
- 在切片中存储指针或带指针的值
- Slice的底层数组被重新分配。比如使用append向容量已满的Slice追加元素,会重新为Slice分配底层数组
- 在interface类型上调用方法。因为interface类型调用方法都是动态调度,只有在运行的时候才能知道真正的实现
优化
sync.Pool