Golang - 逃逸分析案例
1.函数返回局部指针变量
func Add(x,y int) *int {
res := 0
res = x + y
return &res
}
func main() {
Add(1,2)
}
函数返回局部变量是一个指针变量,该函数执行结束,对应栈帧就会销毁,但是引用返回到函数外部,如果外部解析地址,就会导致程序访问非法内存,所以经过编辑器分析过后将其在堆上分配
2.interface类型逃逸
1)interface产生逃逸
func main() {
str := "荔枝"
fmt.Println(str)
}
str是main的一个局部变量,传给 fmt.Printl()
之后逃逸,因为fmt.Println()
的入参是interface{}
类型,那么编译期间就很难确定参数类型
2)指向栈对象的指针不能在堆中
func main() {
str := "苏珊"
fmt.Println(&str)
}
这次str也逃逸到堆上面了,在堆上面进行分配,因为入参是interface,变量str
的地址被以实参的方式传入fmt.Println
被装箱到一个interface{}
装箱的形参变量要在堆上分配,但是还需要存储一个栈上的地址,这和之前说的第一条不符,所以str也会分配到堆上
3.闭包产生逃逸
func Increase() func() int {
n := 0
return func() int {
n++
return n
}
}
func main() {
in := Increase()
fmt.Println(in()) // 1
}
因为函数是指针类型,所以匿名函数当做返回值产生逃逸,匿名函数使用外部变量n
,这个n
会一直存在知道in
被销毁
4. 变量大小不确定及栈空间不足引发逃逸
import (
"math/rand"
)
func LessThan8192() {
nums := make([]int, 100) // = 64KB
for i := 0; i < len(nums); i++ {
nums[i] = rand.Int()
}
}
func MoreThan8192(){
nums := make([]int, 1000000) // = 64KB
for i := 0; i < len(nums); i++ {
nums[i] = rand.Int()
}
}
func NonConstant() {
number := 10
s := make([]int, number)
for i := 0; i < len(s); i++ {
s[i] = i
}
}
func main() {
NonConstant()
MoreThan8192()
LessThan8192()
}
栈空间足够不会发生逃逸,但是变量过大,已经超过栈空间,会逃逸到堆上
总结
1)逃逸分析在编译阶段确定哪些变量可以分配在栈中,哪些变量分配在堆上
2)逃逸分析减轻了GC压力,提高程序的运行速度
3)栈上内存使用完毕不需要GC处理,堆上内存使用完毕会交给GC处理
4)函数传参时对于需要修改原对象值,或占用内存比较大的结构体,选择传指针;对于只读的占用内存较小的结构体,选择直接传值能够获得更好的性能
5)根据代码具体分析,尽量减少逃逸代码,减轻GC压力,提高性能
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」