首页 > 系统相关 >golang 内存逃逸 你应该知道的知识

golang 内存逃逸 你应该知道的知识

时间:2024-07-04 16:22:18浏览次数:19  
标签:golang 逃逸 内存 func go inline main

逃逸分析

目录
原文链接: 一文弄懂 Golang 中的内存逃逸

1.为什么要了解内存逃逸

- 内存逃逸是 Go 语言编程中一个特别需要注意的问题,会影响到程序的性能和稳定性。
了解和掌握 Go 语言中的内存逃逸对于编写高性能和可维护的代码至关重要。通过合理的代码设计和优化技巧可以避免不必要的内存逃逸并提高程序的运行效率。

2.什么是逃逸分析

- 指原本应该分配在栈上分配的内存被分配到了堆中,也就意味着函数返回后,这部分内存不会被自动释放,需要等待垃圾回收处理
- go中内存分配的两种方式
- 栈上分配:函数调用时为局部变量分配内存,函数返回时,内存会自动释放
- 堆上分配:通过 new && make & struct{} 等方式动态分配内存,需要GC来回收内存

3.内存逃逸的影响 - 性能和稳定性

- 1.内存占用增加,因为堆内存不会自动释放,导致程序占用的内存不断增加,如长时间运行的程序会导致系统资源消耗增加至耗尽
- 2.性能下降,相较于站分配,堆分配需要消耗更多的 CPU 和 内存资源,所以导致程序运行速度减慢
- 3.程序不稳定,如程序中有大量内存逃逸,会导致GC频繁工作,从而影响程序的稳定性

4.内存逃逸的原因

- 1.变量的生命周期超出了其作用域,当一个变量在函数外部被引用,比如被赋值给一个包级别的变量或者作为返回值,这个变量就会发生逃逸。
- 2.大对象的分配,对于大型的数据结构,Go 有时会选择在堆上分配内存,即使它们没有在函数外部被引用。
- 3.闭包引用,如果一个函数返回一个闭包,并且该闭包引用了函数的局部变量,那么这些变量也会逃逸到堆上。
- 4.接口动态分配,当一个具体类型的变量被赋值给接口类型时,由于接口的动态特性,具体的值可能会发生逃逸。
- 5.切片和 map 操作,如果对切片进行操作可能导致其重新分配内存,或者向 map 中插入数据,这些操作可能导致逃逸。

5.内存逃逸的检测

- CMD:  go build -gcflags '-m' main.go
- 通过 go tool pprof 分析内存使用,结合运行时的 mem cpu 检测分析内存逃逸现象

6.如何避免内存逃逸

- 1.严格限制变量的作用域。如果一个变量只在函数内部使用,就不要将其返回或赋值给外部变量。
- 2.使用值而不是指针,当不必要的时候,尽量使用值传递而不是指针传递。
- 3.池化对象,对于频繁创建和销毁的对象,考虑使用对象池技术进行复用,减少在堆上分配和回收对象的次数。
- 4.尽量避免在循环或频繁调用的函数中创建闭包,以减少外部变量的引用和堆分配,避免使用不必要的闭包,闭包可能会导致内存逃逸。
- 5.优化数据结构,使用固定大小的数据结构,避免使用动态大小的切片和 map。比如使用数组而不是切片,因为数组的大小在编译时就已确定。
- 6.预分配切片和 map 的容量,如果知道切片或 map 的大小,预先分配足够的容量可以避免在运行时重新分配内存。

7.内存逃逸代码示例

package main

import "strconv"

func main() {
	// _ = varsOutCall()
	//hugeObjectAlloc()
	//_ = closedCall()
	//interfaceDyCall()
	//operateMap()
}


// 1.变量外部引用
func varsOutCall() *int {
	a := 123
	return &a
}
/**
$ go build -gcflags '-m' main.go
# command-line-arguments
.\main.go:9:6: can inline varsOutCall
.\main.go:3:6: can inline main
.\main.go:4:17: inlining call to varsOutCall
.\main.go:10:2: moved to heap: a
 */

// 2.大对象分配
func hugeObjectAlloc()  {
	_ = make([]int64, 1000000)
}
/**
$ go build -gcflags '-m' main.go
# command-line-arguments
.\main.go:24:6: can inline hugeObjectAlloc
.\main.go:3:6: can inline main
.\main.go:5:17: inlining call to hugeObjectAlloc
.\main.go:10:6: can inline varsOutCall
.\main.go:5:17: make([]int64, 1000000) escapes to heap
.\main.go:11:2: moved to heap: a
.\main.go:25:10: make([]int64, 1000000) escapes to heap
 */

// 3.闭包引用
func closedCall( ) func() {
	a := 123
	return func() {
		_ = a
	}
}
/**
$ go build -gcflags '-m' main.go
# command-line-arguments
.\main.go:41:6: can inline closedCall
.\main.go:43:9: can inline closedCall.func1
.\main.go:3:6: can inline main
.\main.go:6:16: inlining call to closedCall
.\main.go:11:6: can inline varsOutCall
.\main.go:25:6: can inline hugeObjectAlloc
.\main.go:6:16: func literal does not escape
.\main.go:12:2: moved to heap: a
.\main.go:26:10: make([]int64, 1000000) escapes to heap
.\main.go:43:9: func literal escapes to heap
 */

// 4.接口动态分配,可能逃逸
type animal interface {
	eat()
}

type dog struct {
	name string
}

func (d dog) eat()  {

}

func interfaceDyCall()  {
	var d animal = &dog{"xiaoming"}
	d.eat()
}

// 5.切片和map操作,可能逃逸
func operateMap()  {
	m := make(map[string]int, 1000)
	for i := 0; i < 1000000; i++ {
		m[strconv.Itoa(i)] = i
	}

}

标签:golang,逃逸,内存,func,go,inline,main
From: https://www.cnblogs.com/davis12/p/18284084

相关文章

  • cpu内存硬盘之间的工作原理!
    在现代计算机系统中,CPU(中央处理器)、内存(RAM)和硬盘(硬盘驱动器或固态硬盘)是三大核心组成部分。它们之间的协同工作关系直接影响整个计算机系统的性能和效率。为了更好地理解计算机的工作原理,CPU、内存和硬盘的基本功能及它们之间的相互交互。这三者的工作原理及其之间的关系。CP......
  • 面试官:Java类是如何被加载到内存中的?
    面试连环callJava类是如何被加载到内存中的?Java类的生命周期都有哪些阶段?JVM加载的class文件都有哪些来源?JVM在加载class文件时,何时判断class文件的格式是否符合要求?类生命周期一个类从被加载到虚拟机内存开始,到卸载出内存为止,它的整个生命周期将会经历加载、验证、准备、......
  • python自动化内存管理
    引用在编程中,引用是指用来标识、访问或操作某个对象的值的标识符或变量。我们可以将引用看作是对象的别名,通过引用可以操作对象,包括读取、修改和传递对象的值。举例来说,假设我们有一个字符串对象`name`,我们可以创建一个变量`person`来引用这个字符串对象。在这个例子中,`perso......
  • PHP反序列化字符逃逸详解
    这段时间遇到几个关于反序列化的字符逃逸的程序,今天来分享一下经验。<?phpfunctionfilter($str){returnstr_replace('bb','ccc',$str);}classA{public$name='aaaa';public$pass='123456';}$AA=newA();$res=filter(serialize($AA));$c=unserial......
  • GaussDB(DWS)性能调优,解决DM区大内存占用问题
    本文分享自华为云社区《GaussDB(DWS)性能调优:DM区优化案例——维度表关联条件存在会计期》,作者:O泡果奶~。当前DM(P1、P3、CBGDM)存在维度表与主表关联时使用会计期作为关联条件,会导致出现大内存占用或未识别数据倾斜的问题【场景一】f.period_id=维度表.period_id1.1、【问题......
  • Linux进程间的通信方式(二)System V 共享内存
    文章目录前言1.共享内存的概念1.1什么是共享内存1.2linux的内存管理机制1.3内存映射2.共享内存的接口分类3.共享内存的相关操作函数3.1ftok函数(获取一个key值)3.2shmget函数(创建或获取一个共享内存描述符)3.3shmat函数(映射共享内存地址空间)3.4shmdt函数(......
  • 鸿蒙内核源码分析(共享内存) | 进程间最快通讯方式
    运行机制共享好端端的一词,近些年被玩坏了,共享单车,共享充电宝,共享办公室,共享雨伞…甚至还有共享女朋友,真是人有多大胆,共享有多大产。但凡事太尽就容易恶心到人,自己也一度被 共享内存 恶心到了,一直不想碰它,拖到了现在才写。共享内存的原理简单,目的是为了进程间通讯,方法......
  • golang 打印类型和switch case选择
    因为需要打印传输信息,要用到pion的stats,而stats返回报告的又包含多个type,对于不同type有不同的数据统计,所以需要打印类型和switchcase针对于不同的type执行不同的命令。针对于返回变量类型,golang采用reflect包,对于变量valua,可以利用reflect.TypeOf(valua)返回变量类型,而这只能做......
  • Java循环创建对象内存溢出怎么解决
    在Java中,如果在循环中不当地创建大量对象而不及时释放内存,很容易导致内存溢出(OutOfMemoryError)。这通常发生在以下几种情况中:(1)循环内不断创建对象但对象引用未被释放:对象被创建后,如果它们一直被引用(即使是间接的),垃圾收集器(GC)就无法回收它们占用的内存。(2)循环次数过多或对象体积......
  • Java循环创建对象内存溢出怎么解决
    在Java中,如果在循环中不当地创建大量对象而不及时释放内存,很容易导致内存溢出(OutOfMemoryError)。这通常发生在以下几种情况中:(1)循环内不断创建对象但对象引用未被释放:对象被创建后,如果它们一直被引用(即使是间接的),垃圾收集器(GC)就无法回收它们占用的内存。(2)循环次数过多或对象体......