深析Golang变量逃逸(Escape Analysis)
来源:云海天教程
时间:2022-12-30 15:47:28 248浏览 收藏
知识点掌握了,还需要不断练习才能熟练运用。下面golang学习网给大家带来一个Golang开发实战,手把手教大家学习《深析Golang变量逃逸(Escape Analysis)》,在实现功能的过程中也带大家重新温习相关知识点,温故而知新,回头看看说不定又有不一样的感悟!
在讨论变量生命周期之前,先来了解下计算机组成里两个非常重要的概念:堆和栈。变量的生命周期我们将在下一节《变量生命周期》中为大家讲解。什么是栈
栈(Stack)是一种拥有特殊规则的线性表数据结构。1) 概念
栈只允许从线性表的同一端放入和取出数据,按照后进先出(LIFO,Last InFirst Out)的顺序,如下图所示。图:栈的操作及扩展
往栈中放入元素的过程叫做入栈。入栈会增加栈的元素数量,最后放入的元素总是位于栈的顶部,最先放入的元素总是位于栈的底部。
从栈中取出元素时,只能从栈顶部取出。取出元素后,栈的元素数量会变少。最先放入的元素总是最后被取出,最后放入的元素总是最先被取出。不允许从栈底获取数据,也不允许对栈成员(除了栈顶部的成员)进行任何查看和修改操作。
栈的原理类似于将书籍一本一本地堆起来。书按顺序一本一本从顶部放入,要取书时只能从顶部一本一本取出。
2) 变量和栈有什么关系
栈可用于内存分配,栈的分配和回收速度非常快。下面的代码展示了栈在内存分配上的作用:func calc(a, b int) int { var c int c = a * b var x int x = c * 10 return x}
代码说明如下:第 1 行,传入 a、b 两个整型参数。第 2行,声明整型变量c,运行时,c 会分配一段内存用以存储 c 的数值。第 3行,将 a 和 b 相乘后赋值给 c。第 5行,声明整型变量x,x 也会被分配一段内存。第 6行,让 c 乘以 10 后赋值给变量x。第 8 行,返回 x 的值。
上面的代码在没有任何优化的情况下,会进行变量c 和 x 的分配过程。Go语言默认情况下会将 c 和 x 分配在栈上,这两个变量在 calc() 函数退出时就不再使用,函数结束时,保存 c 和 x 的栈内存再出栈释放内存,整个分配内存的过程通过栈的分配和回收都会非常迅速。
什么是堆
堆在内存分配中类似于往一个房间里摆放各种家具,家具的尺寸有大有小,分配内存时,需要找一块足够装下家具的空间再摆放家具。经过反复摆放和腾空家具后,房间里的空间会变得乱七八糟,此时再往这个空间里摆放家具会发现虽然有足够的空间,但各个空间分布在不同的区域,没有一段连续的空间来摆放家具。此时,内存分配器就需要对这些空间进行调整优化,如下图所示。图:堆的分配及空间
堆分配内存和栈分配内存相比,堆适合不可预知大小的内存分配。但是为此付出的代价是分配速度较慢,而且会形成内存碎片。
变量逃逸(Escape Analysis)——自动决定变量分配方式,提高运行效率
堆和栈各有优缺点,该怎么在编程中处理这个问题呢?在 C/C++ 语言中,需要开发者自己学习如何进行内存分配,选用怎样的内存分配方式来适应不同的算法需求。比如,函数局部变量尽量使用栈,全局变量、结构体成员使用堆分配等。程序员不得不花费很长的时间在不同的项目中学习、记忆这些概念并加以实践和使用。Go语言将这个过程整合到了编译器中,命名为“变量逃逸分析”。通过编译器分析代码的特征和代码的生命周期,决定应该使用堆还是栈来进行内存分配。
1) 逃逸分析
通过下面的代码来展现Go语言如何使用命令行来分析变量逃逸,代码如下:package mainimport "fmt"// 本函数测试入口参数和返回值情况func dummy(b int) int { // 声明一个变量c并赋值 var c int c = b return c}// 空函数, 什么也不做func void() {}func main() { // 声明a变量并打印 var a int // 调用void()函数 void() // 打印a变量的值和dummy()函数返回 fmt.Println(a, dummy(0))}
代码说明如下:第 6 行,dummy() 函数拥有一个参数,返回一个整型值,用来测试函数参数和返回值分析情况。第 9 行,声明变量c,用于演示函数临时变量通过函数返回值返回后的情况。第 16 行,这是一个空函数,测试没有任何参数函数的分析情况。第 23 行,在 main() 中声明变量a,测试 main() 中变量的分析情况。第 26 行,调用 void() 函数,没有返回值,测试 void() 调用后的分析情况。第 29 行,打印 a 和 dummy(0) 的返回值,测试函数返回值没有变量接收时的分析情况。
接着使用如下命令行运行上面的代码:
go run -gcflags "-m -l" main.go
使用 go run 运行程序时,-gcflags 参数是编译参数。其中 -m 表示进行内存分配分析,-l 表示避免程序内联,也就是避免进行程序优化。运行结果如下:
# command-line-arguments
./main.go:29:13: a escapes to heap
./main.go:29:22: dummy(0) escapes to heap
./main.go:29:13: main ... argument does not escape
0 0
上面例子中变量 c 是整型,其值通过 dummy() 的返回值“逃出”了 dummy() 函数。变量 c 的值被复制并作为 dummy() 函数的返回值返回,即使变量c 在 dummy() 函数中分配的内存被释放,也不会影响 main() 中使用 dummy() 返回的值。变量 c 使用栈分配不会影响结果。
2) 取地址发生逃逸
下面的例子使用结构体做数据,来了解结构体在堆上的分配情况,代码如下:package mainimport "fmt"// 声明空结构体测试结构体逃逸情况type Data struct {}func dummy() *Data { // 实例化c为Data类型 var c Data //返回函数局部变量地址 return &c}func main() { fmt.Println(dummy())}
代码说明如下:第 6 行,声明一个空的结构体做结构体逃逸分析。第 9 行,将 dummy() 函数的返回值修改为 *Data 指针类型。第 12 行,将变量c 声明为 Data 类型,此时 c 的结构体为值类型。第 15 行,取函数局部变量 c 的地址并返回。第 20 行,打印 dummy() 函数的返回值。
执行逃逸分析:
go run -gcflags "-m -l" main.go
# command-line-arguments
./main.go:15:9: &c escapes to heap
./main.go:12:6: moved to heap: c
./main.go:20:19: dummy() escapes to heap
./main.go:20:13: main ... argument does not escape
&{}
Go语言最终选择将 c 的 Data 结构分配在堆上。然后由垃圾回收器去回收 c 的内存。
3) 原则
在使用Go语言进行编程时,Go语言的设计者不希望开发者将精力放在内存应该分配在栈还是堆的问题上,编译器会自动帮助开发者完成这个纠结的选择,但变量逃逸分析也是需要了解的一个编译器技术,这个技术不仅用于Go语言,在 Java 等语言的编译器优化上也使用了类似的技术。编译器觉得变量应该分配在堆和栈上的原则是:变量是否被取地址;变量是否发生逃逸。
理论要掌握,实操不能落!以上关于《深析Golang变量逃逸(Escape Analysis)》的详细介绍,大家都掌握了吧!如果想要继续提升自己的能力,那么就来关注golang学习网公众号吧!
-
499 收藏
-
378 收藏
-
116 收藏
-
452 收藏
-
121 收藏
-
357 收藏
-
228 收藏
-
279 收藏
-
163 收藏
-
481 收藏
-
292 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习