Golang——逃逸分析

逃逸分析是指由编译器决定内存分配到堆上还是栈上。当我们在函数中申请了一个新的对象:

  • 如果分配到栈中,则函数执行结束后可自动将内存回收。
  • 如果分配到堆中,则函数执行结束后,不会自动将内存释放掉,需要GC在进行清除。

逃逸分析使Go语言能够将局部变量返回,程序员不需要担心局部变量分配到栈上还是堆上。并且Go程序员不需要像C程序员一样,担心内存泄漏的问题。

逃逸策略:

在函数中申请一个新的对象时,编译器会根据该对象是否被函数外部引用来确定是否逃逸。

  • 如果变量在函数外部没有引用,则优先放到栈上。
  • 如果变量在函数外部存在引用,则必定放到堆上。

注意,对于一个仅在函数内部使用的变量,也有可能放到堆上,比如内存过大超过栈的存储能力。我们很难通过肉眼直接判断函数内部的变量是否发生了逃逸,我们需要借助go build -gcflags=-m指令判断是否发生逃逸分析。

逃逸场景:

**1.函数返回指针,会发生指针逃逸。**如果我们的返回的结构体中包含指针,也会发生逃逸,比如slice、 map。

Go 复制代码
func main() {
	getNum()
}
func getNum() *int {
	i := 1
	return &i
}


# ExpertProgramming/chapter04/escape/move_to_heap
./main.go:6:6: can inline getNum
./main.go:3:6: can inline main
./main.go:4:8: inlining call to getNum
./main.go:7:2: moved to heap: i

2.栈空间不足以存放当前创建的对象时,会将新创建的对象分配到堆上,发生逃逸。

Go 复制代码
func main() {
	s := make([]int, 1000000, 1000001)
	for index, value := range s {
		fmt.Println(index, value)
	}
}

./main.go:10:14: inlining call to fmt.Println
./main.go:8:11: make([]int, 1000000, 1000001) escapes to heap
./main.go:10:14: ... argument does not escape
./main.go:10:15: index escapes to heap
./main.go:10:22: value escapes to heap

3.在编译期间很难确定其对象的具体类型时,会发生逃逸。比如参数为interface类型的函数

Go 复制代码
func main() {
	s := "wang"
	fmt.Println(s)
}

./main.go:9:13: inlining call to fmt.Println
./main.go:9:13: ... argument does not escape
./main.go:9:14: s escapes to heap

4.闭包的引用对象

Go 复制代码
func main() {
	num := Fibonacci()
	for i := 0; i < 10; i++ {
		fmt.Println(num())
	}
}

func Fibonacci() func() int {
	a, b := 0, 1
	return func() int {
		a, b = b, a+b
		return a
	}
}

./main.go:10:14: ... argument does not escape
./main.go:10:18: ~R0 escapes to heap
./main.go:15:2: moved to heap: a
./main.go:15:5: moved to heap: b
./main.go:16:9: func literal escapes to heap

5.切片长度不确定的时候也会发生逃逸,即使这时候我们不返回切片

Go 复制代码
func main() {
	getSlice()
}

func getSlice() {
	length := 3
	s1 := make([]int, length)
	s1[0] = 9
}

./main.go:7:6: can inline getSlice
./main.go:3:6: can inline main
./main.go:4:10: inlining call to getSlice
./main.go:4:10: make([]int, length) escapes to heap
./main.go:9:12: make([]int, length) escapes to heap

总结:

逃逸分析的目的是为了确定对象分配到了堆上还是分配到了栈上。有了逃逸分析,我们可以非常方便地返回函数里面的变量,这方便了我们的开发,但也有可能会造成我们程序的性能下降。因为分配到堆上的变量,需要GC进行回收。而GC运行的时候,会发生STW。所以,我们要减少不合理的逃逸现象,减轻GC的负担。

相关推荐
Mikko7几秒前
JVM 线上排查实战(七):jps 看不到进程、jstack 报不允许的操作怎么办?attach 失败的六种情况实测
java·运维·jvm·后端
阡陌数智11 分钟前
RAG 系统的召回退化:向量库、分块、重排全链路问题排查与优化实践
开发语言·人工智能·语言模型·自然语言处理·推荐算法
小宋102113 分钟前
Agent 工具升级如何不破坏线上:Tool Schema 版本兼容与契约测试
java·人工智能·后端·spring
汤米粥17 分钟前
后端开发主流技术方案
java·python·golang·php·nodejs·后端开发
geovindu19 分钟前
rust: Flyweight Pattern
开发语言·后端·设计模式·rust·享元模式·结构型模式
我的xiaodoujiao20 分钟前
Django 基础知识详细图文教程 13-Django 模型定义与使用 3
数据库·后端·python·测试工具·oracle·django
JWASX21 分钟前
Java 转 go 学习 - Redis(1)
学习·golang
SimonKing22 分钟前
QClaw关停之后:一个工具的退场,一段关系的告别
java·后端·程序员
JWASX23 分钟前
Java 转 go 学习 - Redis(2)
学习·golang
Escalating_xu37 分钟前
【C 语言】字符函数和字符串函数:从 ctype 到 strtok、strerror 全面解析
java·c语言·开发语言