【Golang】什么是内存逃逸?

news/2024/11/23 2:27:45/

文章目录

  • 要从C/C++谈起
  • Golang的内存逃逸


要从C/C++谈起

在C/C++中,局部变量被分配到栈区,一旦当前函数执行完毕,局部变量占用的内存也将被释放,因此以下代码无法将数组的内容传递出去。

int *getArray() {int array[7] = {1, 2, 3, 4, 5, 6, 7};return array;
}int main() {int *array = getArray();for (int i = 0; i < 7; i++) {cout << array[i] << ", ";}return 0;
}

因为,当getArray()执行完毕后,array数组的内存就被释放了,返回的array指针是一个野指针。再访问这个指针得到的是不确定的、无意义的数据。

解决办法是利用动态内存分配,使用new关键字申请一个堆区的内存(C使用malloc,C++使用new),将以上代码中定义数组的行替换为:

int *array = new int[7]{1, 2, 3, 4, 5, 6, 7};

这样就可以把getArray()中创建的数组传递出去,使用完毕后,编写delete[] array;释放内存。

Golang的内存逃逸

不同于C/C++,Golang的内存分配是完全由编译器自动管理的,开发者无法干预。

在Golang中,内存逃逸指的是在函数中分配的局部变量或对象,由于其生命周期需要延长或在函数外部继续使用,导致编译器将其分配到堆区而不是栈区的情况。这种情况下,变量或对象的生命周期超出了原本的作用域,需要在堆上分配内存以保证数据的有效性。

Go编译器在编译时,会尽量将变量分配到栈区,以提高内存的访问速度。但是,如果编译器无法确定变量的声明周期是否会超出作用域,就会将其分配到堆上,以确保数据访问的有效性。这种情况就被称为内存逃逸。简单说,就是局部变量被分配到了堆区。

当函数外部对指针没有引用时,优先分配在栈上。以下是一些触发内存逃逸的情况:

  1. 在函数中返回指针:如果在函数中创建一个局部变量,然后返回它的指针,那么这个变量很可能会逃逸到堆上,因为它需要在函数退出后仍然可访问。
  2. 在函数中开启 goroutine:如果在函数内部开启了一个 goroutine,并将局部变量传递给这个 goroutine,这个变量可能会逃逸,因为 goroutine 可能在函数退出后继续访问该变量。
  3. 变量被闭包使用:如果一个闭包引用了外部函数的局部变量,这个变量也可能会逃逸到堆上,因为闭包可能会在函数退出后继续存在。
  4. 变量占用空间太大:如果一个局部变量很大,超过了栈的大小限制,编译器可能会将其分配到堆上,以避免栈溢出。

"内存逃逸"听起来好像"有什么东西跑掉了"一样,乍一听给人一种不好的信号。实际上它并不是太值得关注的问题。

内存逃逸通常不会引发大问题,因为Go的垃圾回收器会自动管理内存。当然,使用栈上的内存更具有性能,如果你特别在意这种性能的话,以下是一些避免内存逃逸的方法

  1. 避免闭包: 闭包可能导致变量的生命周期延长,从而导致内存逃逸。尽量避免在闭包中使用外部变量。
  2. 避免返回指针或引用: 返回指向局部变量的指针或引用会导致内存逃逸。(Go有三个引用类型:slice,map,chan)
  3. 返回数组而不是切片(slice):数组是值类型的,切片是引用类型的。
  4. 使用值类型的接收器(receiver): 当定义方法时,如果不需要修改接收器的状态,尽量使用值类型的接收器而不是指针接收器,可以减少内存逃逸的可能性。
  5. 使用编译器分析工具:可以使用go build -gcflags="-m"命令来触发编译器的逃逸分析报告。这会在编译过程中输出逃逸分析的结果,帮助我们了解哪些变量逃逸到了堆上。

http://www.ppmy.cn/news/1061072.html

相关文章

【C++精华铺】9.STL string

目录 1. string类的优势 2. string类的常用接口 2.1 常用构造 1. 空串构造&#xff1a;string(); 2. C串构造&#xff1a;string(const char* s); 3. 拷贝构造&#xff1a;string(const string& str); 4. 字符填充构造&#xff1a;string(size_t n, char c); 5. 迭代…

前端面试的计算机网络部分(4)每天10个小知识点

目录 系列文章目录前端面试的计算机网络部分&#xff08;1&#xff09;每天10个小知识点前端面试的计算机网络部分&#xff08;2&#xff09;每天10个小知识点前端面试的计算机网络部分&#xff08;3&#xff09;每天10个小知识点 知识点31. **IPv4 和 IPv6的区别**32. 在前端开…

Web安全测试(一):HTTP请求详解

一、前言 结合内部资料,与安全渗透部门同事合力整理的安全测试相关资料教程,全方位涵盖电商、支付、金融、网络、数据库等领域的安全测试,覆盖Web、APP、中间件、内外网、Linux、Windows多个平台。学完后一定能成为安全大佬! 全部文章请访问专栏:《全栈安全测试教程(0基…

Spearman Footrule距离

Spearman Footrule距离是一种用于衡量两个排列之间差异的指标。它衡量了将一个排列变换为另一个排列所需的操作步骤&#xff0c;其中每个操作步骤都是交换相邻元素。具体而言&#xff0c;Spearman Footrule距离是每个元素在两个排列中的排名差的绝对值之和。 这个指标的名字中…

【Focal Loss】解决类别不平衡问题,增加对困难样本的挖掘

Focal Loss是在交叉熵损失函数的基础上增加了一个平衡因子 α \alpha α和一个聚焦因子 γ \gamma γ&#xff0c;分别用来调节不同类别样本的权重以及难分样本和易分样本之间的权重一个样本的交叉熵损失函数如下&#xff1a; p t p_t pt​表示将该样本分类为t的概率一个样本的…

vue 学习笔记 简单实验

1.代码(html) <script src"https://unpkg.com/vuenext" rel"external nofollow" ></script> <div id"counter">Counter: {{ counter }} </div> <script> const Counter {data() {return {counter: 5}} } Vue.cr…

ELK + Kibana + Logstash实现可视化日志

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;elasticsearch、kibana、logstash、日志收集、日志可视化☀️每日 一言&#xff1a;坚持就是胜利啊&#xff0c;哥~ 一、前言 面试官&#xff1a;在日常开发工作中你们是如何查看日志的呢&#x…

Redis常用数据类型及常用命令

文章目录 简介下载与安装运行访问服务端运行连接数据库设置数据库密码带端口密码地址连接数据库利用可视化工具连接redis 5种常用数据类型Redis常用命令字符串操作命令哈希操作命令列表操作命令集合操作命令有序集合操作命令通用命令 简介 下载与安装 https://github.com/micro…