举一个经典的Go语言内存逃逸的一个例子

70 阅读1分钟

代码

main.go 文件

package main

import "fmt"

func main() {
   data := map[string]int{
      "a": 1,
      "b": 2,
      "c": 3,
      "d": 4,
   }
   res := make(map[string]*int, 4)
   for k, v := range data {
      res[k] = &v
   }
   for k, v := range res {
      fmt.Println(k, *v)
   }
}

上述代码的输出结果是什么?

a 4
b 4
c 4
d 4

解释

上述代码中,发生了内存逃逸

for k, v := range data { 
    res[k] = &v 
}

因为for 循环遍历过程中,v为指针变量,并且在for循环结束后还需要用到 res[k] = &v 。因此会发生内存逃逸,v会被分配到对空间上。每次遍历过程中,v都指向同一块内存地址,因此res[k]所有的value值为最后一次遍历的v。

查看逃逸分析

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

$ go build -gcflags "-m -l" main.go
# command-line-arguments
.\main.go:13:9: moved to heap: v
.\main.go:6:24: map[string]int{...} does not escape
.\main.go:12:13: make(map[string]*int, 4) does not escape
.\main.go:17:14: ... argument does not escape
.\main.go:17:14: k escapes to heap
.\main.go:17:18: *v escapes to heap

第三行 .\main.go:13:9: moved to heap: v,局部变量v被分配到了堆空间上。

【注】最后两行显示发生了内存逃逸,这是因为fmt.Println()的函数的参数是空接口,也会发生内存逃逸。