golang学习笔记(内存逃逸分析)

2024-05-03 08:04

本文主要是介绍golang学习笔记(内存逃逸分析),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

golang的内存逃逸

逃逸分析( Escape analysis) 是指由编译器决定内存分配的位置, 不需要程序员指定。 函数中申请一个新的对象。

  • 如果分配在栈中, 则函数执行结束可自动将内存回收;
  • 如果分配在堆中, 则函数执行结束可交给GC( 垃圾回收) 处理;

内存逃逸策略

每当函数中申请新的对象, 编译器会跟据该对象是否被函数外部引用来决定是否逃逸:

  • 如果函数外部没有引用, 则优先放到栈中
  • 如果函数外部存在引用, 则必定放到堆中;

注意, 对于函数外部没有引用的对象, 也有可能放到堆中, 比如内存过大超过栈的存储能力。

逃逸场景分析

指针逃逸

Go可以返回局部变量指针, 这其实是一个典型的变量逃逸案例, 示例代码如下:

package maintype Student struct {Name stringage int
}func NewStudent(name string, age int) *Student {stu := new(Student)stu.Name = namestu.age = agereturn stu
}
func main()  {NewStudent("abcd", 24)
}

函数NewStudent()内部stu为局部变量, 其值通过函数返回值返回, stu本身为一指针, 其指向的内存地址不会是栈而是堆, 这就是典型的逃逸案例。

通过编译参数-gcflags=-m可以查年编译过程中的逃逸分析:
运行结果: 请注意运行结果中出现了escapes to heap,也就是发生了内存逃逸现象。
在这里插入图片描述

栈空间不足逃逸

分析一下下面代码会不会产生内存逃逸现象

package maintype Student struct {Name stringage int
}func Slice()  {s := make([]int, 1000, 1000)for index, _ := range s {s[index] = index}
}
func main()  {Slice()
}

上面代码Slice()函数中分配了一个1000个长度的切片, 是否逃逸取决于栈空间是否足够大。 直接查看编译提示, 如下可见并没有发生逃逸:
在这里插入图片描述
但是如果长度扩大10倍呢?那情况会怎么样?

package mainfunc Slice()  {s := make([]int, 10000, 10000)for index, _ := range s {s[index] = index}
}
func main()  {Slice()
}

结果:
在这里插入图片描述
我们发现当切片长度扩大到10000时就会逃逸。实际上当栈空间不足以存放当前对象时或无法判断当前切片长度时会将对象分配到堆中

动态类型逃逸

很多函数参数为interface类型, 比如fmt.Println(a …interface{}), 编译期间很难确定其参数的具体类型,也人产生逃逸。 如下代码所示:

package mainimport "fmt"func main()  {s := "abcd"fmt.Println(s)
}

结果:上述代码s变量只是一个string类型变量, 调用fmt.Println()时会产生逃逸。
在这里插入图片描述

闭包引用对象逃逸

相信刷题的同学对这代码是十分的熟悉:

package mainimport "fmt"func Fibonacci() func() int {a, b := 0, 1return func() int {a, b = b, a + breturn a}
}
func main()  {res := Fibonacci()for i := 0; i < 10; i++ {fmt.Printf("Print Result is %d\n" , res())}
}

这段代码的运行结果如下:
在这里插入图片描述
但是Fibonacci()函数中原本属于局部变量的a和b由于闭包的引用, 不得不将二者放到堆上, 以致产生逃逸:
在这里插入图片描述

总结

  • 栈上分配内存比在堆中分配内存有更高的效率
  • 栈上分配的内存不需要GC处理
  • 堆上分配的内存使用完毕会交给GC处理
  • 逃逸分析目的是决定内分配地址是栈还是堆
  • 逃逸分析在编译阶段完成

这篇关于golang学习笔记(内存逃逸分析)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Linux中的more 和 less区别对比分析

《Linux中的more和less区别对比分析》在Linux/Unix系统中,more和less都是用于分页查看文本文件的命令,但less是more的增强版,功能更强大,:本文主要介绍Linu... 目录1. 基础功能对比2. 常用操作对比less 的操作3. 实际使用示例4. 为什么推荐 less?5.

spring-gateway filters添加自定义过滤器实现流程分析(可插拔)

《spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔)》:本文主要介绍spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔),本文通过实例图... 目录需求背景需求拆解设计流程及作用域逻辑处理代码逻辑需求背景公司要求,通过公司网络代理访问的请求需要做请

Java集成Onlyoffice的示例代码及场景分析

《Java集成Onlyoffice的示例代码及场景分析》:本文主要介绍Java集成Onlyoffice的示例代码及场景分析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 需求场景:实现文档的在线编辑,团队协作总结:两个接口 + 前端页面 + 配置项接口1:一个接口,将o

golang float和科学计数法转字符串的实现方式

《golangfloat和科学计数法转字符串的实现方式》:本文主要介绍golangfloat和科学计数法转字符串的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望... 目录golang float和科学计数法转字符串需要对float转字符串做处理总结golang float

golang实现延迟队列(delay queue)的两种实现

《golang实现延迟队列(delayqueue)的两种实现》本文主要介绍了golang实现延迟队列(delayqueue)的两种实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录1 延迟队列:邮件提醒、订单自动取消2 实现2.1 simplChina编程e简单版:go自带的time

Golang实现Redis分布式锁(Lua脚本+可重入+自动续期)

《Golang实现Redis分布式锁(Lua脚本+可重入+自动续期)》本文主要介绍了Golang分布式锁实现,采用Redis+Lua脚本确保原子性,持可重入和自动续期,用于防止超卖及重复下单,具有一定... 目录1 概念应用场景分布式锁必备特性2 思路分析宕机与过期防止误删keyLua保证原子性可重入锁自动

golang 对象池sync.Pool的实现

《golang对象池sync.Pool的实现》:本文主要介绍golang对象池sync.Pool的实现,用于缓存和复用临时对象,以减少内存分配和垃圾回收的压力,下面就来介绍一下,感兴趣的可以了解... 目录sync.Pool的用法原理sync.Pool 的使用示例sync.Pool 的使用场景注意sync.

golang中slice扩容的具体实现

《golang中slice扩容的具体实现》Go语言中的切片扩容机制是Go运行时的一个关键部分,它确保切片在动态增加元素时能够高效地管理内存,本文主要介绍了golang中slice扩容的具体实现,感兴趣... 目录1. 切片扩容的触发append 函数的实现2. runtime.growslice 函数gro

golang实现动态路由的项目实践

《golang实现动态路由的项目实践》本文主要介绍了golang实现动态路由项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习... 目录一、动态路由1.结构体(数据库的定义)2.预加载preload3.添加关联的方法一、动态路由1

IDEA下"File is read-only"可能原因分析及"找不到或无法加载主类"的问题

《IDEA下Fileisread-only可能原因分析及找不到或无法加载主类的问题》:本文主要介绍IDEA下Fileisread-only可能原因分析及找不到或无法加载主类的问题,具有很好的参... 目录1.File is read-only”可能原因2.“找不到或无法加载主类”问题的解决总结1.File