golang中的内存分配有栈(stack) 和堆(heap)
- 栈分配: 分配速度快,只需要CPU的两个指令,PUSH【分配】和 RELEASE【释放】
- 堆分配: 分配速度较慢,首先需要找到一块大小合适的内存块,之后还需要GC垃圾回收才能释放
golang中常见的内存逃逸场景
-gcflags "-m -l" 查看变量逃逸
- 函数内部将局部变量指针返回,被外部调用,其生命周期大于栈,溢出
type User struct {}
func NewUser() *User{
return &User{}
}
func main(){
_ = NewUser()
}
- 对象太大,超过栈帧大小
func main(){
_ = make([]int,0,1000)
_ = make([]int,0,100000) // 超出范围,逃逸
}
- 闭包引用逃逸
func f() func() int{
a:=1
return func() int{
return a
}
}
func main(){
f()
}
- 动态类型逃逸
因为fmt.Println函数参数类型是interface{},在interface 类型上调用方法都是动态调用的 ---- 方法的实现只能在运行时知道
func main(){
a:=1
fmt.Println("a: ",a)
}
- 在切片上存储指针或带指针的指。比如[]*string,导致切片内容逃逸,其引用值一直在堆上
func main(){
ch:=make(chan *string,1)
ch<-new(string)
}
如何避免内存逃逸
- 对于性能要求比较高且访问频次比较高的函数调用,应该尽量避免使用接口类型
- 不要盲目使用变量指针作为参数,虽然减少了复制,但变量逃逸的开销更大
- 预先设定好slice长度,避免频繁超出容量,重新分配