操作系统(OS)层面

栈内存与堆内存的区别
栈内存一般由操作系统分配与释放;堆内存一般由程序自身申请与释放
栈内存一般存放函数参数、函数返回值、局部变量、函数调用时的临时上下文等;堆内存一般存放全局变量
栈内存比堆内存访问速度更快
每个线程分配一个栈内存;每个进程分配一个堆内存
栈内存创建时,内存大小是固定的,越界则会发生stack overflow错误;堆内存创建时,内存大小不固定,可随程序运行增加或减少
栈内存是由高地址向低地址增长;堆内存是由低地址向高地址增长

Golang内存管理

基础信息
Golang是自己管理内存,不依赖操作系统,即向操作系统申请一块较大内存,然后自己决定将变量分配到栈空间或对空间
分配选择
基本同上面的分配原则,但对于函数的引用参数会有一些特殊。如果编译器无法证明函数返回之后变量是否仍然被引用,此时就必须在堆空间分配该变量,随后采用垃圾回收机制管理,而从避免指针悬空。此外,如果局部变量过大,也会选择分配在堆空间
总结:最终的分配空间在于编译器的选择,编译器分析变量的生存周期的过程就叫做逃逸分析
栈内存
栈内存的分配与释放全权由操作系统决定,开发者无法控制。一般栈内存会自动创建,函数返回的时候内存会被自动释放。栈内存的分配与释放速度较快
堆内存
对存有由于不确定大小,因此代价就是分配速度较慢且会形成内存碎片。堆内存不能自动被编译器释放,只能通过垃圾回收器才能释放

内存逃逸

把函数内的局部变量通过指针形式返回
发送指针或带有指针的值到channel中
在切片中存储指针或带指针的值
Slice的底层数组被重新分配。比如使用append向容量已满的Slice追加元素,会重新为Slice分配底层数组
在interface类型上调用方法。因为interface类型调用方法都是动态调度,只有在运行的时候才能知道真正的实现

优化

尽可能避免内存逃逸,因为栈内存的效率远高于堆内存
无法避免的逃逸现象,对于频繁的内存申请操作,可以试着重用内存。比如通过sync.Pool