golang内存逃逸的学习笔记

 更新时间:2024年05月05日 09:03:07   作者:淡墨@~无痕  
内存逃逸是 Go 语言编程中一个特别需要注意的问题,会影响到程序的性能和稳定性,本文主要介绍了golang内存逃逸的学习笔记,感兴趣的可以了解一下

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

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

内存逃逸策略

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

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

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

逃逸场景分析

指针逃逸

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

package main

type Student struct {
	Name string
	age int
}

func NewStudent(name string, age int) *Student {
	stu := new(Student)
	stu.Name = name
	stu.age = age
	return stu
}
func main()  {
	NewStudent("abcd", 24)
}

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

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

在这里插入图片描述

栈空间不足逃逸

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

package main

type Student struct {
	Name string
	age int
}

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

上面代码Slice()函数中分配了一个1000个长度的切片, 是否逃逸取决于栈空间是否足够大。 直接查看编译提示, 如下可见并没有发生逃逸:

在这里插入图片描述

但是如果长度扩大10倍呢?那情况会怎么样?

package main

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

结果:

在这里插入图片描述

我们发现当切片长度扩大到10000时就会逃逸。实际上当栈空间不足以存放当前对象时或无法判断当前切片长度时会将对象分配到堆中

动态类型逃逸

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

package main

import "fmt"

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

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

在这里插入图片描述

闭包引用对象逃逸

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

package main

import "fmt"

func Fibonacci() func() int {
	a, b := 0, 1
	return func() int {
		a, b = b, a + b
		return 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内存逃逸的学习笔记的文章就介绍到这了,更多相关Golang内存逃逸 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go基于struct tag实现结构体字段级别的访问控制

    Go基于struct tag实现结构体字段级别的访问控制

    本文将会基于这个主题展开,讨论Go中的结构体tag究竟是什么,我们该如何利用它,另外,文末还提供了一个实际案例,实现结构体字段级别的访问,帮助我们进一步提升对struct tag的理解
    2024-02-02
  • golang 输出重定向:fmt Log,子进程Log,第三方库logrus的详解

    golang 输出重定向:fmt Log,子进程Log,第三方库logrus的详解

    这篇文章主要介绍了golang 输出重定向:fmt Log,子进程Log,第三方库logrus的详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Go语言实现IP段范围校验示例

    Go语言实现IP段范围校验示例

    这篇文章主要介绍了Go语言实现IP段范围校验示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • Golang实现IO操作

    Golang实现IO操作

    本文主要介绍了Golang实现IO操作,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-05-05
  • Go chassis云原生微服务开发框架应用编程实战

    Go chassis云原生微服务开发框架应用编程实战

    这篇文章主要为大家介绍了Go chassis云原生微服务开发框架应用编程实战示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • 用gin开发的golang项目三种开发模式方式

    用gin开发的golang项目三种开发模式方式

    这篇文章主要介绍了用gin开发的golang项目三种开发模式方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • Go语言中三个输入函数(scanf,scan,scanln)的区别解析

    Go语言中三个输入函数(scanf,scan,scanln)的区别解析

    本文详细介绍了Go语言中三个输入函数Scanf、Scan和Scanln的区别,包括用法、功能和输入终止条件等,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2024-10-10
  • go语言中的return语句

    go语言中的return语句

    这篇文章主要介绍了go语言中的return语句,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下,希望对你的学习有所帮助
    2022-05-05
  • Go语言中工作池的原理与实现

    Go语言中工作池的原理与实现

    工作池是一种并发编程模式,它使用一组固定数量的工作线程来执行任务队列中的工作单元,本文将介绍工作池的工作原理,并通过代码示例演示其在实际应用中的用途,有需要的可以参考下
    2023-10-10
  • Go语言实现生成样式美观的PDF文件

    Go语言实现生成样式美观的PDF文件

    使用 Go 语言生成样式美观的 PDF 文件是一个常见的需求,尤其是在报告生成、发票、合同等场景中,下面就跟随小编一起来学习一下具体实现方法吧
    2025-01-01

最新评论