热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入理解Go逃逸分析

HowdoIknowwhetheravariableisallocatedontheheaporthestack?Fromacorrectnessstandpoint,youdon

How do I know whether a variable is allocated on the heap or the stack?

From a correctness standpoint, you don’t need to know. Each variable in Go exists as long as there are references to it. The storage location chosen by the implementation is irrelevant to the semantics of the language.

The storage location does have an effect on writing efficient programs. When possible, the Go compilers will allocate variables that are local to a function in that function’s stack frame. However, if the compiler cannot prove that the variable is not referenced after the function returns, then the compiler must allocate the variable on the garbage-collected heap to avoid dangling pointer errors. Also, if a local variable is very large, it might make more sense to store it on the heap rather than the stack.

In the current compilers, if a variable has its address taken, that variable is a candidate for allocation on the heap. However, a basic escape analysis recognizes some cases when such variables will not live past the return from the function and can reside on the stack.

在Go里面定义了一个变量,到底是分配在堆上还是栈上,Go官方文档告诉我们,不需要管,他们会分析,其实这个分析就是逃逸分析

在编程语言的编译优化原理中,分析指针动态范围的方法称之为逃逸分析。通俗来讲,当一个对象的指针被多个方法或线程引用时,我们称这个指针发生了逃逸。

发生逃逸行为的情况主要有两种:

  • 方法逃逸:当一个对象在方法中定义之后,作为参数传递或返回值到其它方法中
  • 线程逃逸:如类变量或实例变量,可能被其它线程访问到

这里主要对 方法逃逸 进行分析,通过逃逸分析来判断一个变量到底是分配在堆上还是栈上

逃逸策略
  • 如果编译器不能证明某个变量在函数返回后不再被引用,则分配在堆上
  • 如果一个变量过大,则有可能分配在堆上
分析目的
  • 不逃逸的对象分配在栈上,则变量在用完后就会被编译器回收,从而减少GC的压力
  • 栈上的分配要比堆上的分配更加高效
  • 同步消除,如果定义的对象上有同步锁,但是栈在运行时只有一个线程访问,逃逸分析后如果在栈上则会将同步锁去除
逃逸场景

指针逃逸

在 build 的时候,通过添加 -gcflags “-m” 编译参数就可以查看编译过程中的逃逸分析

在有些时候,因为变量太大等原因,我们会选择返回变量的指针,而非变量,这里其实就是逃逸的一个经典现象

func main() {
test()
}
func test() *int {
i := 1
return &i
}

逃逸分析结果:

# command-line-arguments
./main.go:7:6: can inline test
./main.go:3:6: can inline main
./main.go:4:6: inlining call to test
./main.go:4:6: main &i does not escape
./main.go:9:9: &i escapes to heap
./main.go:8:2: moved to heap: i

可以看到最后两行指出,变量 i 逃逸到了 heap

栈空间不足逃逸

首先,我们尝试创建一个 长度较小的 slice

func main() {
stack()
}
func stack() {
s := make([]int, 10, 10)
s[0] = 1
}

逃逸分析结果:

./main.go:12:6: can inline stack
./main.go:3:6: can inline main
./main.go:4:7: inlining call to stack
./main.go:4:7: main make([]int, 10, 10) does not escape
./main.go:13:11: stack make([]int, 10, 10) does not escape

结果显示未逃逸

然后,我们创建一个超大的slice

func main() {
stack()
}
func stack() {
s := make([]int, 100000, 100000)
s[0] = 1
}

逃逸分析结果:

./main.go:12:6: can inline stack
./main.go:3:6: can inline main
./main.go:4:7: inlining call to stack
./main.go:4:7: make([]int, 100000, 100000) escapes to heap
./main.go:13:11: make([]int, 100000, 100000) escapes to heap

这时候就逃逸到了堆上了

动态类型逃逸

func main() {
dynamic()
}
func dynamic() interface{} {
i := 0
return i
}

逃逸分析结果:

./main.go:18:6: can inline dynamic
./main.go:3:6: can inline main
./main.go:5:9: inlining call to dynamic
./main.go:5:9: main i does not escape
./main.go:20:2: i escapes to heap

这里的动态类型逃逸,其实在理解了interface{}的内部结构后,还是可以归并到 指针逃逸 这一类的,有兴趣的同学可以看一下 《深入理解Go的interface》

闭包引用逃逸

func main() {
f := fibonacci()
for i := 0; i <10; i++ {
f()
}
}
func fibonacci() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}

逃逸分析结果:

./main.go:11:9: can inline fibonacci.func1
./main.go:11:9: func literal escapes to heap
./main.go:11:9: func literal escapes to heap
./main.go:12:10: &b escapes to heap
./main.go:10:5: moved to heap: b
./main.go:12:13: &a escapes to heap
./main.go:10:2: moved to heap: a
参考

《Go 逃逸分析》


推荐阅读
  • GreenDAO快速入门
    前言之前在自己做项目的时候,用到了GreenDAO数据库,其实对于数据库辅助工具库从OrmLite,到litePal再到GreenDAO,总是在不停的切换,但是没有真正去了解他们的 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文介绍了使用Spark实现低配版高斯朴素贝叶斯模型的原因和原理。随着数据量的增大,单机上运行高斯朴素贝叶斯模型会变得很慢,因此考虑使用Spark来加速运行。然而,Spark的MLlib并没有实现高斯朴素贝叶斯模型,因此需要自己动手实现。文章还介绍了朴素贝叶斯的原理和公式,并对具有多个特征和类别的模型进行了讨论。最后,作者总结了实现低配版高斯朴素贝叶斯模型的步骤。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 本文介绍了Oracle存储过程的基本语法和写法示例,同时还介绍了已命名的系统异常的产生原因。 ... [详细]
  • Java 11相对于Java 8,OptaPlanner性能提升有多大?
    本文通过基准测试比较了Java 11和Java 8对OptaPlanner的性能提升。测试结果表明,在相同的硬件环境下,Java 11相对于Java 8在垃圾回收方面表现更好,从而提升了OptaPlanner的性能。 ... [详细]
author-avatar
黄镇郁皓伟嘉惟
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有