Golang内存分配逃逸分析

Golang内存分配逃逸分析 - Jxy 博客

总结

内存分配逃逸就是 栈的内存逃逸到堆上,需要gc清理,耗性能

如:

  • 指针逃逸在方法内把局部变量指针返回
  • 栈空间不足逃逸(空间开辟过大)
  • 动态类型逃逸(不确定长度大小)(在 interface 类型上调用方法)
  • 发送指针或带有指针的值到 channel 中

参考博客

问题

  • 知道golang的内存逃逸吗?什么情况下会发生内存逃逸

    1、golang的内存分配方式分为栈(stack)和堆(heap)两种;栈廉堆贵

    分配到栈内存的好处:函数返回时会直接释放,不会引起垃圾回收,对性能没有影响

    分配到堆内存的坏处:会引起gc垃圾回收,影响程序性能

    2、其中 而发生内存逃逸是指:如果变量的内存发生逃逸,它的生命周期就是不可知的,其会被分配到堆上,而堆上分配内存不能像栈一样会自动释放,需要go本身的gc垃圾回收机制释放,会影响程序的运行性能

    3、大白话就是(函数内)(局部)变量的分配从栈跑到堆上

    ## 网上说辞一
    因为如果变量的内存发生逃逸,
    它的生命周期就是不可知的,其会被分配到堆上,
    而堆上分配内存不能像栈一样会自动释放,为了解放程序员双手,专注于业务的实现,
    go实现了gc垃圾回收机制,但gc会影响程序运行性能,所以要尽量减少程序的gc操作
    
    ## 网上说辞二
    golang程序变量会携带有一组校验数据,
    用来证明它的整个生命周期是否在运行时完全可知。
    如果变量通过了这些校验,
    它就可以在栈上分配。
    否则就说它 逃逸了,必须在堆上分配。
    

关于堆和栈

  • 先说说golang中内存分配方式:

    主要是堆(heap)栈(stack)分配两种

    栈分配廉价,堆分配昂贵

    栈分配:对于栈的操作只有入栈和出栈两种指令,属于静态资源分配

    堆分配:堆中分配的空间,在结束使用之后需要垃圾回收器进行闲置空间回收,属于动态资源分配

    使用栈分配:函数的内部中不对外开放的局部变量,只作用于函数中

    使用堆分配:1.函数的内部中对外开放的局部变量

                      2.变量所需内存超出栈所提供最大容量
    

逃逸场景(什么情况才分配到堆中)

  • 指针逃逸在方法内把局部变量指针返回
  • 栈空间不足逃逸(空间开辟过大)
  • 动态类型逃逸(不确定长度大小)(在 interface 类型上调用方法)
  • 发送指针或带有指针的值到 channel 中

指针逃逸:在方法内把局部变量指针返回

  • 描述

    局部变量原本应该在栈中分配,函数返回后生命周期结束,直接回收,但是由于返回时被外部引用,因此其生命周期大于栈,则溢出,就会分配到堆

  • 举例

    package main
    
    type student struct {
      Name string
      Age int
    }
    func escapes1() *student{
      // 逃逸分析:原stu不是指针,但是返回了&stu;结果:moved to heap(堆): stu
      // stu:=student{}
      // return &stu
      // 逃逸分析:原stu是指针;结果:escapes to heap(堆)
      //stu := new(student)
      stu:=&student{}
      return stu
    }
    func main() {
      escapes1()
    }
    
  • 解决

    返回值类型而不是指针类型即可;将返回值 *student 改为 student

栈空间不足逃逸(空间开辟过大)

  • 描述

    实际上当栈空间不足以存放当前对象时或无法判断当前切片长度时会将对象分配到堆中。

    例如创建一个超大的slice;是否逃逸取决于栈空间是否足够大

  • 举例

    func escapes2(){
        //当切片长度和容量扩大到10000时就会逃逸,本机测试8192临界值
        //是否逃逸取决于栈空间是否足够大
      s:=make([]int,8191,8192)
      for i := range s {
          s[i]=i
      }
    }
    func main() {
      escapes2()
    }
    
  • 解决

    创建适量的slice

动态类型逃逸(不确定长度大小)(在 interface 类型上调用方法)

  • 描述

    很多函数参数为interface类型,比如fmt.Println(a …interface{})

  • 举例

    func main() {
        // 在 interface 类型上调用方法
      a:=1
      fmt.Println(a)
        // 堆 动态分配不定空间 逃逸
        b := 20
      c := make([]int, 0, b) 
    }
    
  • 解决

闭包引用对象逃逸

  • 描述

  • 举例

    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 < 10; i++ {
          fmt.Printf("Fibonacci: %d\n", f())
      }
    }
    
  • 解决

发送指针或带有指针的值到 channel 中

  • 描述

    在编译时,是没有办法知道哪个 goroutine 会在 channel 上接收数据。

    所以编译器没法知道变量什么时候才会被释放

  • 举例

  • 解决

在一个切片上存储指针或带指针的值

  • 描述

    一个典型的例子就是 []*string 。这会导致切片的内容逃逸。

    尽管其后面的数组可能是在栈上分配的,但其引用的值一定是在堆上。

  • 举例

  • 解决

slice 的背后数组被重新分配了,因为 append 时可能会超出其容量( cap )

  • 描述

    slice 初始化的地方在编译时是可以知道的,它最开始会在栈上分配。

    如果切片背后的存储要基于运行时的数据进行扩充,就会在堆上分配。

  • 举例

  • 解决

逃逸分析(Escape analysis)

所谓逃逸分析(Escape analysis)是指由编译器决定内存分配的位置,不需要程序员指定。

通过 `go build -gcflags=-m main.go` 查看逃逸的情况
go build -gcflags=-m main.go
# command-line-arguments
./main.go:7:6: can inline foo
./main.go:13:10: inlining call to foo
./main.go:7:10: leaking param: s
./main.go:8:10: new(A) escapes to heap   # 发生逃逸
./main.go:16:13: io.Writer(os.Stdout) escapes to heap
./main.go:16:13: c escapes to heap      # 发生逃逸
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容