Go内存管理之代码的逃逸分析
基本上,每种编程语言都有其自己的内存模型。每个变量,常量都存储在内存的某个物理位置上,这些存储位置通过内存指针访问。
至于变量,就是程序里赋予内存存储位置的名称。程序可以根据需要进行操作,并且可以将新值分配给相同的内存存储位置。而常量也是赋予内存存储位置的名称,但是程序不能将新值分配给相同的存储位置(意思就是常量是恒定值,不能被重新赋值)。
我们都知道,程序运行时使用的内存被分为两个区:堆和栈。那么如何得知变量是分配在栈(stack)上还是堆(heap)上呢?对于手动管理内存的语言,比如C
/C++
,使用malloc
或者new
申请的变量会被分配到堆上。但是Go
并不是这样,虽然 Go
语言里面也有new
。
在Go
官网的FAQ 上有一个关于变量分配的问题如下:
❝Q:如何得知变量是分配在栈(stack)上还是堆(heap)上? A: 准确地说,你并不需要知道。Golang 中的变量只要被引用就一直会存活,存储在堆上还是栈上由内部实现决定而和具体的语法没有关系。知道变量的存储位置确实对程序的效率有帮助。如果可能,Golang 编译器会将函数的局部变量分配到函数栈帧(stack frame)上。然而,如果编译器不能确保变量在函数 return 之后不再被引用,编译器就会将变量分配到堆上。而且,如果一个局部变量非常大,那么它也应该被分配到堆上而不是栈上。当前情况下,如果一个变量被取地址,那么它就有可能被分配到堆上。然而,还要对这些变量做逃逸分析,如果函数 return 之后,变量不再被引用,则将其分配到栈上。 ❞
Go
编译器决定变量应该分配到什么地方时会进行逃逸分析。
逃逸分析指的是:Go
编译器会跨越函数和包的边界进行全局的逃逸分析。它会检查是否需要在堆上为一个变量分配内存,还是说可以在栈本身的内存里对其进行管理。
我们可以使用go
命令工具的-gcflags="-m"
选项观察逃逸分析的结果以及GC工具链的内联决策,内联是一种手动或编译器优化,用于将简短函数的调用替换为函数体本身。这么做的原因是它可以消除函数调用本身的开销,也使得编译器能更高效地执行其他的优化策略。我们可以显示地在函数定义前面加一行特殊的注释让编译器不对函数进行内联(看下面的例子)。
接下来我们通过两个例子观察一下Go编译器对程序进行的逃逸分析。
package main
import "fmt"
func main() {
fmt.Println("Called stackAnalysis", stackAnalysis())
}
//go:noinline
func stackAnalysis() int {
data := 55
return data
}
在上面的main函数中,我们对stackAnalysis进行了一个简单的函数调用,函数stackAnalysis返回int值。通过命令观察一下它的逃逸分析:
go build -gcflags "-m -l"
./scratch.go:4:14: "Called stackAnalysis" escapes to heap
./scratch.go:4:51: stackAnalysis() escapes to heap
./scratch.go:4:13: main ... argument does not escape
编译器的逃逸分析告诉我们:
- 第4行14个字符处的字符串标量"Called stackAnalysis"逃逸到堆上。
- 第4行51个字符串的函数调用
stackAnalysis()
逃逸到了堆上。
❝"escapes to heap"的意思是变量需要在函数栈之间共享,上面的例子就是在
main
和fmt.Println
之间共享。 ❞
如上图所示,main
和stackAnalysis
函数分配在一个栈上。由于函数具有自己的变量,因此也会将变量分配到栈的某个地方。当函数返回时,与该函数关联的所有变量也会从内存中删除。
接下来再看第二个例子:
package main
import "fmt"
func main() {
fmt.Println("Called heapAnalysis", heapAnalysis())
}
//go:noinline
func heapAnalysis() *int {
data := 55
return &data
}
在上面的主函数中,我们对heapAnalysis
进行了一个简单的函数调用,并且函数heapAnalysis
返回* int
类型的指针。下面是对这段代码的逃逸分析结果:
./scratch.go:9:9: &data escapes to heap
./scratch.go:8:2: moved to heap: data
./scratch.go:4:14: "Called heapAnalysis" escapes to heap
./scratch.go:4:49: heapAnalysis() escapes to heap
./scratch.go:4:13: main ... argument does not escape
当函数heapAnalysis
返回* int
类型的指针变量后, 会在main
函数中使用该指针变量,因为是在heapAnalysis
函数外部访问,变量必须被移动到堆上。
如上图所示,main
和heapAnalysis
函数分配在一个栈上。由于函数具有自己的变量,因此也会将变量分配到栈的某个地方。但是编译器检查到该值是返回了它的指针,并且已用于另一个函数,因此变量被移到了堆中,主函数会从堆中访问该变量。
简单来说,逃逸分析也是了解我们应该如何优化应用程序性能的一种方式。通过上面的分析可以看出来,虽然指针能够减少变量在函数间传递时的数据值拷贝问题,但是也不应该所有类型的数据都应该返回其指针。如果分配到堆上的共享变量太多的话也无形中增加了GC
的压力。
- 微信服务号模板消息接口新增"设置行业"和"添加模板"及细节优化
- WPF备忘录(3)如何从 Datagrid 中获得单元格的内容与 使用值转换器进行绑定数据的转换IValueConverter
- WPF备忘录(2)WPF获取和设置鼠标位置与progressbar的使用方法
- WPF文字修饰——上、中、下划线与基线
- 微信公众平台数据接口正式对所有认证公众号开放
- 参考基因组没有,经费也没那么多,怎么办?
- .Net下SQLite的DBHelp
- 数据库进程间通信解决方案之MQ
- 【学术】算法交易的神经网络:强化经典策略
- java.util.logging 例子
- WPF命令(Command)介绍、命令和数据绑定集成应用
- lncRNA实战项目-第六步-WGCNA相关性分析
- 【项目】Github上的一个简单项目:用人工智能预测大学录取概率
- lncRNA实战项目-第五步-差异表达的mRNA和lncRNA
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python创建目录文件夹
- python 按顺序读文件夹下面的文件
- python剪切文件
- 一个hashCode问题的追问,差点让我陷入无底洞
- 【JAVA基础&高级】 面向对象篇
- MySQL-InnoDb行格式与数据页结构 Krains 2020-08-08
- 《自然语言处理实战入门》 ---- 第4课 :中文分词原理及相关组件简介 之 汉语分词领域主要分词算法、组件、服务(上)...
- MySQL索引 Krains 2020-08-09
- 「查缺补漏」巩固你的Redis知识体系
- MySQL事务 Krains 2020-08-09
- Linux本地提权漏洞复现与检测思路
- 内容安全策略( CSP )
- [译] 优化 React APP 的 10 种方法
- 如何免登陆观看b站大会员番剧
- 聊聊越来越火的对象存储