Golang内存逃逸

堆内存(Heap):一般来讲是人为手动进行管理,手动申请、分配、释放。一般硬件内存有多大堆内存就有多大。适合不可预知大小的内存分配,分配速度较慢,而且会形成内存碎片。
栈内存(Stack):是一种拥有特殊规则的线性表数据结构。由编译器进行管理,自动申请、分配、释放。大小一般是固定的。

作者:我是一只鱼吖
链接:https://juejin.cn/post/7140864963974791175
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

在 Go 语言(Golang)中,编译器会尝试在栈上分配局部变量,因为栈上分配的速度更快,并且能够在函数返回时自动回收。但是,如果编译器发现某个变量的生命周期超出了其函数的范围,它就会将该变量分配到堆上,这个过程称为“逃逸分析”。

逃逸分析 是编译器在编译期间进行的一种分析,用来确定变量的生命周期是否超出了函数的作用域。

可以看看这个
https://juejin.cn/post/6992178559208914957

什么是内存逃逸?

逃逸值得是在函数内部创建的对象或者变量,在函数结束后,仍然被其它部分引用或者持有

对内存管理的理解

堆内存一般来说是人为手动进行管理,手动申请分配和释放的。一般硬件内存有多大,堆内存就有多大,适合不可预知大小的内存分配,分配速度较慢,而且会形成内存碎片。(在 Go 语言中,因为 Go 有 GC 自动管理,所以程序员不需要手动释放内存)

栈内存是一种有特殊规则的线性表数据结构,由编译器进行管理,自动申请,分配和释放,大小一般是固定的,它的分配速度非常快,因为只需要移动栈顶指针

内存逃逸带来的影响

当对象或者变量等一些本应在站上分配的变量,因为其生命周期超出了函数范围,不得不分配到堆上。
这时候就会引起

  • 性能开销
    • 堆分配比栈分配慢
    • 垃圾回收也会带来开销
  • 内存碎片
    • 堆内存很容易产生内存碎片
  • 缓存局部性
    • 栈内存通常具有更好的缓存局部性,因为栈上的数据通常是连续分配的,而且生命周期短。
    • 堆内存的分配是分散的,可能导致缓存未命中的问题,影响性能。

导致内存逃逸的原因

  1. 变量的生命周期超出了栈内存的活动范围

  2. 编译时不知道变量大小,因为栈内存的分配需要编译器在编译时知道变量的大小和生命周期
    编译时无法确定变量大小的情况:

    如果变量的大小在编译时无法确定(例如动态数组、切片、映射等),编译器无法在栈上为其分配固定大小的内存。

    此时,编译器会将这些变量分配到堆上,以确保程序能够正常运行。

  3. Golang 的内存分片基本原则

    • 指向栈上对象的指针不能被存储到堆中
    • 指向栈上对象的指针不能超过该栈对象的生命周期