2026-02-14
Go
0
请注意,本文编写于 163 天前,最后修改于 0 天前,其中某些信息可能已经过时。

目录

逃逸分析

本篇文章主要介绍的是和go底层相关的并且非常重要的知识点,拿起小本本开始吧

逃逸分析

Go 语言中的逃逸分析(Escape Analysis)是编译器在编译阶段进行的一项关键优化。它的核心目的是决定一个变量应该分配在栈(stack)上还是堆(heap)上,以提升程序性能。

🎯 核心目的:提升性能

栈分配:内存分配和释放的开销极小,速度非常快,由编译器自动管理。 堆分配:内存分配开销较大,且需要依赖垃圾回收器(GC)来回收,会给程序带来额外负担。

逃逸分析的目标就是尽可能将变量分配在栈上,从而减少堆内存分配的次数,降低 GC 压力,最终提升程序运行效率。

⚙️ 分析原理:变量是否“逃逸”

编译器会分析变量的作用域和生命周期。如果一个变量的生命周期超出了它被创建的函数作用域,就被认为发生了“逃逸”,必须分配到堆上。

简单来说,判断标准是:这个变量在函数返回后,是否还有可能被访问到?

是 → 发生逃逸,分配到堆。 否 → 未逃逸,分配到栈。

📝 常见的逃逸场景

根据分析,以下几种情况会导致变量逃逸到堆上:

  1. 返回局部变量的指针

这是最典型的逃逸场景。函数返回后,局部变量本应被销毁,但如果返回了它的地址,为了保证指针有效,编译器必须将其分配到堆上。

golang
func newInt() *int { x := 10 // x 是局部变量 return &x // 返回 x 的地址,x 逃逸到堆 }
  1. 向接口赋值

接口变量在底层存储的是一个动态类型和一个动态值。当把一个具体类型的值赋给接口时,这个值需要被“装箱”,通常会分配到堆上。

golang
func printValue() { x := 100 var i interface{} = x // x 被赋值给接口 i,x 逃逸到堆 fmt.Println(i) }
  1. 在闭包中引用外部变量

如果一个闭包(匿名函数)引用了其外部函数的局部变量,并且这个闭包的生命周期可能超过外部函数,那么被引用的变量就会逃逸到堆上。

golang
func counter() func() int { count := 0 // 匿名函数引用了外部变量 count // 当 counter 函数返回后,匿名函数仍可能被调用,所以 count 必须存活 return func() int { count++ return count } }
  1. 创建 slice 或 map 等动态数据结构

虽然 slice 或 map 的头部结构体可能分配在栈上,如果在make时,初始容量很大,这是会分配到堆上

golang
package main func main() { s := make([]int, 100000000) // 底层数组会分配在堆上 s[0] = 100 }

查看逃逸分析

shell
go build -gcflags="-m" other/t1/main.go # command-line-arguments other/t1/main.go:3:6: can inline main other/t1/main.go:4:11: make([]int, 100000000) escapes to heap

如果我们将容量该小一点

golang
package main func main() { s := make([]int, 1000) // 底层数组会分配在堆上 s[0] = 100 }

查看逃逸分析

shell
go build -gcflags="-m" other/t1/main.go # command-line-arguments other/t1/main.go:3:6: can inline main other/t1/main.go:4:11: make([]int, 1000) does not escape

这时就没有逃逸了

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:繁星

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!