什么是逃逸分析
在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法——分析在程序的哪些地方可以访问到指针。也是就是说逃逸分析是解决指针作用范围的编译优化方法。编程中常见的两种逃逸情景:
- 函数中局部对象指针被返回(不确定被谁访问)
- 对象指针被多个子程序(如线程 协程)共享使用
为什么要做逃逸分析
开始我们提到go语言中对象内存的分配不是由语言运算符或函数决定,而是通过逃逸分析来决定。为什么要这么干呢?其实说到底还是为了优化程序。函数中生成一个新对象:
- 如果分配到栈上,待函数返回资源就被回收了
- 如果分配到堆上,函数返回后交给gc来管理该对象资源
栈资源的分配及回收速度比堆要快,所以逃逸分析最大的好处应该是减少了GC的压力。
使用命令go build -gcflags “-m -l” test.go可以分析变量是否逃逸
逃逸分析的场景
指针逃逸
典型的指针逃逸案例,返回局部变量的指针
package main
func test2() *int {
c1 := 20
return &c1
}
func main() {
c2 := test2()
println(*c2)
}
栈空间不足逃逸
package main
func main() {
t := make([]int, 1000)
m := make([]int, 10000)
t[0] = 1
m[0] = 1
}
闭包引用逃逸
package main
func Fibonacci() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}
func main() {
f := Fibonacci()
for i := 0; i < 5; i++ {
println(f())
}
}
动态类型逃逸
当对象不确定大小或者被作为不确定大小的参数时发生逃逸。t的大小是个变量所以会逃逸到堆上。size作为interface{}参数逃逸到堆上
package main
func main() {
var size int = 10
t := make([]int, size)
m := make([]int, 10)
for i := 0; i < size; i++ {
t[i] = i
m[i] = i
}
}
切片或map赋值
在给切片或者map赋值对象指针(与对象共享内存地址时),对象会逃逸到堆上。但赋值对象值或者返回对象值切片是不会发生逃逸的。
package main
func test3() {
var i = 10
var j = 10
s2 := make([]*int, 2)
m2 := make(map[int]*int)
s2[0] = &i
m2[0] = &j
}
func main() {
test3()
}
其实内存逃逸还有其他场景,所以在平时开发中尽量去避免这些情况,可以有效地提高程序的运行效率
|