面经-内存逃逸

145 阅读3分钟

Golang---内存逃逸 - 爱笑的张飞 - 博客园 (cnblogs.com) Go 的内存分配由编译器决定对象的真正存储位置是在栈上还是在堆上,并管理他的生命周期。

栈区:主要存储函数的入参、局部变量、出参当中的资源由编译器控制申请和释放

堆区:内存由程序员自己控制申请和释放,往往存放一些占用大块内存空间的变量,或是存在于函数局部但需供全局使用的变量变量在堆上的分配和回收都比在栈上开销大的多。对于 go 这种带 GC 的语言来说,会增加 gc 压力,同时也容易造成内存碎片。

内存逃逸就是指原本应该是存储在栈上的变量,因为一些原因被分配在了堆上

image.png

  • 内存逃逸的情况:
  1. 变量在函数外部存在引用,必定放在堆中

image.png

  1. 超过 64k 的内存占用放到堆上

  2. make 创建的切片值为指针

  3. 变量在函数外部没有引用,优先放到栈中

  4. 在 interface 类型上调用方法。 在 interface 类型上调用方法时会把interface变量使用堆分配, 因为方法的真正实现只能在运行时知道。

image.png

在Go语言中,我们无法直接判断一个变量是分配在堆上还是栈上,因为这是Go编译器和运行时系统的内部实现细节,并且在代码层面是不可见的。Go语言的内存分配和管理是由运行时系统自动完成的,开发者无需显式地指定变量分配在堆还是栈上。

Go语言中的变量分配方式主要由以下两个因素决定:

  1. 变量的生命周期:如果变量的生命周期超出了当前函数的范围(如全局变量、闭包中的变量),则通常会将其分配在堆上,以确保它的生命周期延长到需要的时候。如果变量的生命周期仅限于当前函数内部,则通常会将其分配在栈上。
  2. 变量的大小:较大的变量通常会被分配在堆上,以避免栈溢出的风险。较小的变量通常会被分配在栈上,以提高访问速度和内存利用率。

虽然我们无法直接判断一个变量的分配位置,但可以根据一些规则和经验来进行推测:

  1. 全局变量和闭包中的变量通常被分配在堆上。
  2. 指针类型的变量通常被分配在堆上,因为它们的生命周期可能超出当前函数的范围。
  3. 较大的结构体或数组可能被分配在堆上,以避免栈溢出的风险。

需要注意的是,这些只是一些常见的情况,具体的内存分配行为仍然取决于编译器和运行时系统的实现策略,可能会因版本和编译参数等因素而有所不同。

在实际编程中,我们应该关注变量的生命周期和大小,而不需要过多关注其分配位置,因为Go语言的内存管理机制会自动处理变量的分配和释放,让我们可以专注于编写高效、可读性好的代码。