让我们一起分析 Go 语言逃逸
熟悉 C / C++ 的让们读者朋友们应该都知道一个进程(应用程序)的虚拟内存空间划分为栈内存区和堆内存区。
栈内存区上对象的起分内存空间是自动分配和销毁的,使用者无需关心。语言逃逸但是让们,堆内存区上对象的起分内存空间是需要使用者自己管理,无形中增加了使用者的语言逃逸心智负担。
因此,让们一些高级语言会支持垃圾回收(GC),起分降低使用者内存管理的语言逃逸心智负担。支持垃圾回收的让们语言可以自动管理堆内存区上对象的内存空间。
Go 语言编译器负责决定把对象分配到栈上或堆上,起分比如一个对象在函数退出后就不可达(没有其他对象引用该对象)时,语言逃逸那就将该对象分配到栈上,让们反之,起分则分配到堆上。语言逃逸
如果一个对象被分配到堆上,就需要 Go 的垃圾回收管理该对象的内存空间。但是高防服务器,垃圾回收是有代价的,它会占用系统开销。
所以,为了更大限度地降低垃圾回收占用的系统资源,提升应用程序本身可使用的系统资源,使用者就需要尽量减少堆内存分配,尽量多地使应用程序使用栈内存分配,尽量避免 Go 编译器通过逃逸分析优化后被分配到栈内存的对象逃逸到堆内存。
2.查看对象是否发生逃逸Go 语言工具链提供了查看对象是否逃逸的方法,我们在执行 go build 时,配合使用参数 -gcflags 开启编译器支持的额外功能,例如:
go build -gcflasg -m -m -l main.go
-m 用于输出编译器的执行细节,包括逃逸分析的执行。-l 用于禁用内联优化。我们通过使用 Go 语言工具链对一段简单的示例代码进行查看对象是否发生逃逸。
func main() {
sum(1, 2)
}
func sum(a, b int) *int {
res := a + b
return &res
}输出结果:
go build -gcflags -m -m -l main.go
# command-line-arguments
./main.go:8:2: res escapes to heap:
./main.go:8:2: flow: ~r0 = &res:
./main.go:8:2: from &res (address-of) at ./main.go:9:9
./main.go:8:2: from return &res (return) at ./main.go:9:2
./main.go:8:2: moved to heap: res阅读上面这段代码,我们发现 sum 函数中的云服务器变量 res 逃逸到堆,也就是说 Go 编译器通过逃逸分析,决定将变量 res 分配到堆空间。
3.逃逸分析的作用Go 语言编译器通过逃逸分析优化,将对象合理分配到栈空间和堆空间。
因为栈内存分配比堆内存分配更快,所以 Go 语言在编译时通过逃逸分析优化将不会发生逃逸的对象优先分配到栈空间。
因此,不仅降低堆空间内存分配的开销,同时,也可以降低垃圾回收占用的系统资源。
4.总结本文我们介绍 Go 语言逃逸分析,它可以帮助使用者合理分配对象的内存空间。
我们知道分配到堆内存空间的对象,会导致 Go 执行垃圾回收,而垃圾回收会占用系统资源,降低应用程序本身可使用的系统资源。
所以,我们在实际项目开发中,可以借助 Go 工具链分析对象是否会发生逃逸,尽量避免一些不必要的对象逃逸。源码库