Go语言变量逃逸分析

什么是逃逸分析

在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法——分析在程序的哪些地方可以访问到指针。也是就是说逃逸分析是解决指针作用范围的编译优化方法。编程中常见的两种逃逸情景:

  • 函数中局部对象指针被返回(不确定被谁访问)
  • 对象指针被多个子程序(如线程 协程)共享使用

为什么要做逃逸分析

开始我们提到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
}

/*
make([]int, 1000) does not escape
make([]int, 10000) escapes to heap
*/

闭包引用逃逸

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
	}
}

/*
make([]int, size) escapes to heap
make([]int, 10) does not escape
*/

切片或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()
}

/*
./test.go:442:6: moved to heap: i
./test.go:443:6: moved to heap: j
./test.go:445:12: make([]*int, 2) does not escape
./test.go:447:12: make(map[int]*int) does not escape
*/

其实内存逃逸还有其他场景,所以在平时开发中尽量去避免这些情况,可以有效地提高程序的运行效率

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值