Go 语言逃逸分析:内存管理的关键

2024-05-24 23:44

本文主要是介绍Go 语言逃逸分析:内存管理的关键,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 前言
  • 1 逃逸分析是什么?
  • 2 逃逸分析的基本思想是什么?
  • 3 逃逸分析的分配原则是什么?
  • 4 如何进行逃逸分析?
  • 5 逃逸分析案例
    • 5.1 变量在函数外存在引用
    • 5.2 引用类型的逃逸
    • 5.3 闭包捕获变量
    • 5.4 变量占用内存较大
  • 6 变量会逃逸到堆上的原因有哪些?
  • 7 如何避免逃逸?
  • 8 逃逸分析的作用有哪些?
  • 9 学习交流

前言

在正式学习逃逸分析时,我们需要提前补充变量的声明周期的概念。


  • 栈(stack):在GO语言中,栈是调用栈(call stack) 的简称。在GO程序运行时,每一个Goroutine单独维护一个自己的栈区仅允许自己使用不能被其他Goroutine使用。一个栈通常包含许多栈帧(stack frame),它描述的是函数之间的调用关系。栈的内存是由编译器自动进行分配释放的。栈区主要存储函数参数局部变量调用函数帧,它们随函数的创建而分配随函数的退出而销毁
  • 堆(heap):与栈(stack)不同的是,堆区的内存是由编译器工程师共同负责管理分配,交给Runtime GC释放。在堆上分配内存时,必须找到一块足够大的内存来存放新的变量数据。在堆上释放内存时,垃圾回收器会扫描内存空间中不被使用的对象并释放其内存。在我们开发过程中,其实考虑内存管理,主要是考虑堆内存的管理。

变量的声明周期与变量作用域的关系

  • 全局变量:它的生命周期与程序的生命周期一致
  • 局部变量:它的生命周期是动态的,从变量创建开始,到变量不再使用结束。
  • 形参和函数的返回值:它们都是属于局部变量,在函数被调用时创建,调用结束时被销毁。

1 逃逸分析是什么?

逃逸分析(Escape Analysis)是一种重要的编译时优化技术,决定将变量分配到 堆(heap)上 还是 栈(stack)上
通过逃逸分析,编译器可以判断变量的生命周期作用范围,从而选择最合适的内存分配方式,以提高程序的性能和减少内存开销


2 逃逸分析的基本思想是什么?

[!warning]- 思考: 如何知道GO变量的生命周期是完全可知的?

  • 判断变量是值类型还是引用类型,值类型是确定的完全可知的,引用类型是不可知的,不知道是否该变量被其他函数使用。
  • 检查变量的生命周期是否是完全可知的,如果是,则在栈上分配内存。
  • 如何检查不是完全可知的,也就是我们说的逃逸,必须在堆上分配内存。

3 逃逸分析的分配原则是什么?

[!warning]- 如何确定参数类型是不确定的?

  • 变量的数据类型采用interface{},编译期无法确定其具体的参数类型,所以分配到堆中。
  • 什么样的数据类型是确定的? 比如:声明了一个确定数据类型int的变量`var num int
  • GO的逃逸分析是在编译期间完成的,编译期间无法确定的参数类型是放在堆中的。
  • 变量在函数外存在引用,则必定放在
  • 变量占用内存较大,则优先放在
  • 变量在函数外部没有引用,则优先放在中。

4 如何进行逃逸分析?

逃逸分析我们可以通过命令查看结果,-gcflags选项用于向 Go 编译器传递编译标志。这些标志可以用来启用或禁用特定的编译器功能,包括逃逸分析

  • 查看基本的逃逸分析和内联信息,适用于一般情况
go build -gcflags="-m" main.go

[!warning]+ 命令解释说明

  • -m:表示输出有关内联(inlining)逃逸分析的信息

  • 查看更详细的优化信息和逃逸分析结果,并禁用内联优化,适用于需要深入调试和分析的情况。
go build -gcflags '-m -m -l'  main.go

[!warning]+ 命令解释说明

  • -m -m:表示多次使用 -m 标志,增加详细程度,会输出更多的优化信息,包括逃逸分析和内联优化的详细信息。
  • -l:表示禁用内联优化。

5 逃逸分析案例

5.1 变量在函数外存在引用

package mainimport "fmt"func createPointer() *int {var x intreturn &x  // x 逃逸到堆上
}func main() {p := createPointer()fmt.Println(*p)
}

[!note]+ 代码解析说明

  • 函数 createPointer 返回了局部变量 x 的地址,这意味着 x 在函数返回后仍然需要存在。
  • 因此,编译器将 x 分配到堆上,并在逃逸分析的输出中提示 &x escapes to heap

5.2 引用类型的逃逸

  • 例如:切片、映射、接口等引用类型的变量,如果它们的底层数据逃逸,则这些变量也会逃逸。
func createSlice() []int {s := make([]int, 10) return s // s 逃逸到堆上 
}

5.3 闭包捕获变量

如果闭包捕获了外部变量,该变量会逃逸到堆上。

func createClosure() func() { var x int return func() { x++ // x 逃逸到堆上 }
}

5.4 变量占用内存较大

func createManySlice() []int {  var s []int  for i := 0; i < 1000; i++ {  s = make([]int, 10)  }  return s // s 逃逸到堆上  
}

6 变量会逃逸到堆上的原因有哪些?

  • 函数返回值:如果返回一个局部变量的指针或引用,该变量会逃逸到堆上。
  • 闭包捕获:如果闭包捕获了外部变量,该变量会逃逸到堆上。
  1. 长生命周期:如果变量的生命周期超出了其作用域,如通过指针或引用传递给其他函数或存储在全局变量中。

7 如何避免逃逸?

避免逃逸,也就是说减少不必要的堆分配。

  • 避免返回局部变量的指针或引用
  • 尽量减少闭包捕获的外部变量
  • 使用值传递而不是指针传递

8 逃逸分析的作用有哪些?

  • 提升内存分配效率:栈上分配比在堆上分配效率更高效,栈上的内存可以自动回收,而堆上的内存需要垃圾回收器管理。
  • 减少垃圾回收开销:减少不必要的堆分配,可以降低垃圾回收的频率和开销。
  • 提高程序性能:优化内存分配,提升程序运行效率。

9 学习交流

为了方便大家一起学习一起进步,我创建了一个学习交流的平台
感兴趣的朋友们可以加我微信:LH913582934,备注:CSDN。

这篇关于Go 语言逃逸分析:内存管理的关键的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/999873

相关文章

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

Go语言中nil判断的注意事项(最新推荐)

《Go语言中nil判断的注意事项(最新推荐)》本文给大家介绍Go语言中nil判断的注意事项,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1.接口变量的特殊行为2.nil的合法类型3.nil值的实用行为4.自定义类型与nil5.反射判断nil6.函数返回的

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景

python中Hash使用场景分析

《python中Hash使用场景分析》Python的hash()函数用于获取对象哈希值,常用于字典和集合,不可变类型可哈希,可变类型不可,常见算法包括除法、乘法、平方取中和随机数哈希,各有优缺点,需根... 目录python中的 Hash除法哈希算法乘法哈希算法平方取中法随机数哈希算法小结在Python中,

Java Stream的distinct去重原理分析

《JavaStream的distinct去重原理分析》Javastream中的distinct方法用于去除流中的重复元素,它返回一个包含过滤后唯一元素的新流,该方法会根据元素的hashcode和eq... 目录一、distinct 的基础用法与核心特性二、distinct 的底层实现原理1. 顺序流中的去重

Go语言代码格式化的技巧分享

《Go语言代码格式化的技巧分享》在Go语言的开发过程中,代码格式化是一个看似细微却至关重要的环节,良好的代码格式化不仅能提升代码的可读性,还能促进团队协作,减少因代码风格差异引发的问题,Go在代码格式... 目录一、Go 语言代码格式化的重要性二、Go 语言代码格式化工具:gofmt 与 go fmt(一)

关于MyISAM和InnoDB对比分析

《关于MyISAM和InnoDB对比分析》:本文主要介绍关于MyISAM和InnoDB对比分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录开篇:从交通规则看存储引擎选择理解存储引擎的基本概念技术原理对比1. 事务支持:ACID的守护者2. 锁机制:并发控制的艺

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM