浅谈go语言内存逃逸现象

 更新时间:2025年10月19日 11:46:10   作者:砖心搬砖  
本文主要介绍了浅谈go语言内存逃逸现象,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

什么是逃逸分析?

Go 的编译阶段发生在您执行 go build、go install 或 go run 命令时,编译器执行的一个静态分析步骤,它通过分析代码的作用域和生命周期,来判断一个局部变量(通常在栈上分配)是否“逃逸”出了它原本的声明范围(比如函数返回后还需要存在)。如果逃逸了,那么这个变量就必须在堆上分配,以便在函数返回后还能被访问。

逃逸分析发生在哪个阶段?

逃逸分析发生在你之前问题中提到的 编译流程的早期阶段,具体是在:

类型检查之后(因为需要类型信息)。

生成中间代码之前。

所以,它是在纯粹的编译时做出的决定,而不是在运行时。

编译器如何决定:栈 vs. 堆?

编译器遵循一个基本原则:只要编译器能证明函数返回后,该变量不再被引用,那么它就会被分配在栈上。否则,就必须分配在堆上。

以下是导致变量逃逸到堆上的常见情况:

  1. 返回局部变量的指针
    这是最典型的例子。如果函数返回了一个局部变量的地址,那么这个局部变量必须在堆上分配,因为它在函数返回后依然有效。
func foo() *int {
    v := 100 // 局部变量 v
    return &v // 返回 v 的地址。v 会逃逸到堆上。
}

func main() {
    p := foo()
    fmt.Println(*p)
}
  1. 将指针存储到全局变量或包级变量中
    局部变量的生命周期超过了函数本身,因此必须分配在堆上。
var global *int

func bar() {
    x := 1 // x 会逃逸到堆上,因为 global 在 bar 函数返回后仍然存在。
    global = &x
}
  1. 发送指针或带有指针的值到 Channel
    Go 的 Channel 是引用类型,发送操作可能导致变量被其他 goroutine 访问,其生命周期无法在编译时确定,因此会逃逸。
type Data struct {
    S string
}

func main() {
    ch := make(chan *Data, 1)
    d := Data{S: "hello"} // d 会逃逸到堆上
    ch <- &d
}
  1. 在切片或 Map 中存储指针
    如果切片或 Map 本身逃逸了(比如被返回了),那么其中存储的指针所指向的变量也会逃逸。
func getMap() map[string]*int {
    m := make(map[string]*int)
    val := 10 // val 会逃逸到堆上
    m["key"] = &val
    return m
}
  1. 由于动态大小或接口调用
    当切片的大小在编译时无法确定(比如根据参数动态分配),或者值被存储到 interface{} 中(因为接口的方法调用是动态的),编译器可能会采取保守策略,让其逃逸。
func createSlice(n int) {
    s := make([]int, n) // 如果 n 非常大或者在编译时未知,s 可能会逃逸
    // ... 使用 s
}

逃逸分析命令

go build -gcflags="-m -l" main.go

总结

从专业角度看,内存逃逸是 Go 语言内存管理系统的核心机制之一:

安全性优先:逃逸分析确保指针始终指向有效内存,避免悬垂指针

编译时决策:在编译阶段确定变量分配位置,而非运行时

性能权衡:在内存安全性和执行效率之间取得平衡

透明性:对开发者透明,但了解机制有助于写出更高效的代码

到此这篇关于浅谈go语言内存逃逸现象的文章就介绍到这了,更多相关go语言内存逃逸内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go panic的三种产生方式细节探究

    Go panic的三种产生方式细节探究

    这篇文章主要介绍了Go panic的三种产生方式细节探究,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • Go语言七篇入门教程二程序结构与数据类型

    Go语言七篇入门教程二程序结构与数据类型

    这篇文章主要为大家介绍了Go语言的程序结构与数据类型,本篇文章是Go语言七篇入门系列文,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2021-11-11
  • Go Uber静态分析工具NilAway使用初体验

    Go Uber静态分析工具NilAway使用初体验

    这篇文章主要介绍了Go Uber静态分析工具NilAway使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • OpenTelemetry-go的SDK使用方法详解

    OpenTelemetry-go的SDK使用方法详解

    这篇文章主要介绍了OpenTelemetry-go的SDK使用方法,OpenTelemetry帮我们实现了相应语言的SDK,所以我们只需要进行调用即可,本文根据官方文档实例讲解,需要的朋友可以参考下
    2022-09-09
  • Apache IoTDB开发系统之Go原生接口方法

    Apache IoTDB开发系统之Go原生接口方法

    这篇文章主要为大家介绍了 Apache IoTDB开发系统之Go原生接口方法,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • Gin使用swagger生成接口文档的代码示例

    Gin使用swagger生成接口文档的代码示例

    Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful Web 服务,它使用 YAML 或 JSON 格式来定义 API 的结构,本文给大家介绍了Gin使用swagger生成接口文档的代码示例,需要的朋友可以参考下
    2024-06-06
  • GoLand利用plantuml生成UML类图

    GoLand利用plantuml生成UML类图

    本文主要介绍了GoLand利用plantuml生成UML类图,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • Go语言同步等待组sync.WaitGroup结构体对象方法详解

    Go语言同步等待组sync.WaitGroup结构体对象方法详解

    这篇文章主要为大家介绍了Go语言同步等待组sync.WaitGroup结构体对象方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • Golang中的time.Duration类型用法说明

    Golang中的time.Duration类型用法说明

    这篇文章主要介绍了Golang中的time.Duration类型用法说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Golang Gorm 更新字段save、update、updates

    Golang Gorm 更新字段save、update、updates

    在gorm中,批量更新操作可以通过使用Update方法来实现,本文主要介绍了Golang Gorm 更新字段save、update、updates,具有一定的参考价值,感兴趣的可以了解一下
    2023-12-12

最新评论