golang内存逃逸

常见内存逃逸情况

1、在方法内把局部变量指针返回,被外部引用,其生命周期大于栈,则溢出。
2、发送指针或带有指针的值到channel,因为编译时候无法知道那个goroutine会在channel接受数据,编译器无法知道什么时候释放。
3、在一个切片上存储指针或带指针的值。比如[]*string,导致切片内容逃逸,其引用值一直在堆上。
4、因为切片的append导致超出容量,切片重新分配地址,切片背后的存储基于运行时的数据进行扩充,就会在堆上分配。
5、在interface类型上调用方法,在Interface调用方法是动态调度的,只有在运行时才知道。

有大神总结了上述情况:多级间接赋值容易导致逃逸

这里的多级间接指的是,对某个引用类对象中的引用类成员进行赋值。Go 语言中的引用类数据类型有 func, interface, slice, map, chan, *Type(指针)

记住公式 Data.Field = Value,如果 Data, Field 都是引用类的数据类型,则会导致 Value 逃逸。这里的等号 = 不单单只赋值,也表示参数传递。

  • []interface{}: data[0] = 100 会导致 100 逃逸
  • map[string]interface{}: data["key"] = "value" 会导致 "value" 逃逸
  • map[interface{}]interface{}: data["key"] = "value" 会导致 keyvalue 都逃逸
  • map[string][]string: data["key"] = []string{"hello"} 会导致切片逃逸
  • map[string]*int: 赋值时 *int 会 逃逸
  • []*int: data[0] = &i 会使 i 逃逸
  • func(*int): data(&i) 会使 i 逃逸
  • func([]string): data([]{"hello"}) 会使 []string{"hello"} 逃逸
  • chan []string: data <- []string{"hello"} 会使 []string{"hello"} 逃逸
  • 以此类推,不一一列举了
func test(i int)        {}
func testEscape(i *int) {}

func main() {
    i, j, m, n := 0, 0, 0, 0
    t, te := test, testEscape // 函数变量

    // 直接调用
    test(m)        // 不逃逸
    testEscape(&n) // 不逃逸
    // 间接调用
    t(i)   // 不逃逸
    te(&j) // 逃逸
}

 

如何避免

1、go语言的接口类型方法调用是动态,因此不能在编译阶段确定,所有类型结构转换成接口的过程会涉及到内存逃逸发生,在频次访问较高的函数尽量调用接口。
2、不要盲目使用变量指针作为参数,虽然减少了复制,但变量逃逸的开销更大。
3、预先设定好slice长度,避免频繁超出容量,重新分配。

 

使用内存逃逸分析工具检查代码

go build  -gcflags '-m -l' main.go

/tmp/main.go:11:9: &i escapes to heap
/tmp/main.go:11:9:  from ~r0 (return) at /tmp/main.go:11:2
/tmp/main.go:10:2: moved to heap: i
/tmp/main.go:16:18: heap() escapes to heap
/tmp/main.go:16:18:     from ... argument (arg to ...) at /tmp/main.go:16:13
/tmp/main.go:16:18:     from *(... argument) (indirection) at /tmp/main.go:16:13
/tmp/main.go:16:18:     from ... argument (passed to call[argument content escapes]) at /tmp/main.go:16:13
/tmp/main.go:16:13: main ... argument does not escape

日志中的 &i escapes to heap 表示该变量数据逃逸到了堆上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值