Golang---内存逃逸 - 爱笑的张飞 - 博客园 (cnblogs.com) Go 的内存分配由编译器决定对象的真正存储位置是在栈上还是在堆上,并管理他的生命周期。
栈区:主要存储函数的入参、局部变量、出参当中的资源由编译器控制申请和释放
堆区:内存由程序员自己控制申请和释放,往往存放一些占用大块内存空间的变量,或是存在于函数局部但需供全局使用的变量。变量在堆上的分配和回收都比在栈上开销大的多。对于 go 这种带 GC 的语言来说,会增加 gc 压力,同时也容易造成内存碎片。
内存逃逸就是指原本应该是存储在栈上的变量,因为一些原因被分配在了堆上。
- 内存逃逸的情况:
- 变量在函数外部存在引用,必定放在堆中
-
超过 64k 的内存占用放到堆上
-
make 创建的切片值为指针
-
变量在函数外部没有引用,优先放到栈中
-
在 interface 类型上调用方法。 在 interface 类型上调用方法时会把interface变量使用堆分配, 因为方法的真正实现只能在运行时知道。
在Go语言中,我们无法直接判断一个变量是分配在堆上还是栈上,因为这是Go编译器和运行时系统的内部实现细节,并且在代码层面是不可见的。Go语言的内存分配和管理是由运行时系统自动完成的,开发者无需显式地指定变量分配在堆还是栈上。
Go语言中的变量分配方式主要由以下两个因素决定:
- 变量的生命周期:如果变量的生命周期超出了当前函数的范围(如全局变量、闭包中的变量),则通常会将其分配在堆上,以确保它的生命周期延长到需要的时候。如果变量的生命周期仅限于当前函数内部,则通常会将其分配在栈上。
- 变量的大小:较大的变量通常会被分配在堆上,以避免栈溢出的风险。较小的变量通常会被分配在栈上,以提高访问速度和内存利用率。
虽然我们无法直接判断一个变量的分配位置,但可以根据一些规则和经验来进行推测:
- 全局变量和闭包中的变量通常被分配在堆上。
- 指针类型的变量通常被分配在堆上,因为它们的生命周期可能超出当前函数的范围。
- 较大的结构体或数组可能被分配在堆上,以避免栈溢出的风险。
需要注意的是,这些只是一些常见的情况,具体的内存分配行为仍然取决于编译器和运行时系统的实现策略,可能会因版本和编译参数等因素而有所不同。
在实际编程中,我们应该关注变量的生命周期和大小,而不需要过多关注其分配位置,因为Go语言的内存管理机制会自动处理变量的分配和释放,让我们可以专注于编写高效、可读性好的代码。