一 什么是内存逃逸
????????Golang中,内存的分配有两种方式:堆(Heap)和栈(Stack);栈是计算机内存的特定区域,一般是 CPU 自动分配释放,读写很快,更重要的是不会产生碎片;堆是应用程序在运行的时候请求操作系统分配的,需要申请和释放,会产生碎片。他们直接的区别和联系,见这篇文章:什么是堆?什么是栈?他们之间有什么区别和联系
????????那么什么是内存逃逸呢?简单说,就是指从栈上的分配变为从堆上分配。Go在编译的时候会进行逃逸分析,来决定是放栈上还是放堆上,不逃逸的对象放栈上,可能逃逸的放堆上。
二 逃逸场景
???????? 那么在什么情况下会发生内存逃逸呢?这个就是本篇文章要探讨的。在探讨之前,需要说明的是,验证某个函数的变量是否发生逃逸,我这里使用命名:go run -gcflags "-m -l" (-m 打印逃逸分析信息,-l 禁止内联编译);
1 指针逃逸
????????Go在方法内将局部变量指针返回,如:
package main
type demoStr struct {
demo string
}
func newDemoStr(v string) *demoStr {
t := new(demoStr)
t.demo = v
return t
}
func main() {
newDemoStr("This is a test demo")
}
???????? 结果如下:
2 切片扩容或容量太大,栈空间不足
package main
func main() {
s1 := make([]int, 1000000, 1000000)
_ = s1
}
???????? 结果如下:
3 在 slice 或 map 中存储指针
package main
func main() {
var x int32 = 10
var ls []*int32
ls = append(ls, &x)
}
???????? 结果如下:
4 发送指针或带有指针的值到 channel 中
package main
func main() {
ch1 := make(chan int, 1)
x := 30
ch1 <- x
ch2 := make(chan *int, 1)
y := 30
py := &y
ch2 <- py
}
???????? 结果如下:
5 Interface类型多态
6 闭包引用包外的值
三 如何避免
1 对于少量的数据,使用传值而不是传指针
2 尽量避免使用长度不固定的 slice 切片,因为在编译期无法确定切片长度,只能将切片使用堆分配
3 性能要求比较高且访问频次比较高的函数调用,谨慎使用 interface 调用方法
参考文档
https://zhuanlan.zhihu.com/p/438812101
https://juejin.cn/post/6952177646050639902
https://cloud.tencent.com/developer/article/1732263
https://mp.weixin.qq.com/s?__biz=MzAxMTA4Njc0OQ%3D%3D&mid=2651437818&idx=1&sn=4f1e08a9b73b70c347033778b2d56b82&scene=45#wechat_redirect
|