golang——内存逃逸机制

        一般来说,局部变量会在函数返回后被销毁,因此被返回的引用就成为了没有指针指向的引用,程序会进入未知状态                                                                                                          但这在go中是安全的,go语言会对每个局部变量进行逃逸分析,如果发生局部变量的作用域超出该函数,则不会将内存分配在栈上,而是分配在堆上,即使释放函数,其内容也不会受影响
概念: go在 编译时 进行逃逸分析,他会决定一个对象放在栈上还是堆上,不逃逸的放栈上,可能逃逸的放堆上
目的:尽可能将变量分配到栈上
方式: 编译器可以证明变量在函数返回后不再被引用,才会分配到栈上,否则分配到堆上
  • 上分配(静态内存分配),一般由系统进行申请和释放,eg.函数的入参、局部变量、返回值等,每个函数都会分配一个栈帧,在函数运行结束后进行销毁
  • 上分配(动态内存分配),在函数运行结束后仍然可以使用,如果要回收掉,需要进行GC,带来额外的性能开销

编程语言不断优化GC算法,主要目的是为了减少GC带来的额外性能开销,变量一旦逃逸会导致性能开销变大

逃逸机制:

编译器会根据变量是否被外部引用来决定是否逃逸

1.函数没有外部引用,优先放到栈中

2.函数外部存在引用,放在堆中

3.栈上放不下,必定放到堆中

逃逸分析就是由编译器决定哪些变量放在栈中,哪些放在堆中,通过编译参数-gcflag=-m可以查看编译过程中的逃逸分析

(二)可能的场景:

通过go build -gcflags=-m main.go查看逃逸情况

1.指针逃逸

函数返回值为局部变量的指针,函数虽然退出了,但因为指针的存在,指向的内存不能随着函数结束而回收,因此只能分配在堆上

package main
func escape1()* int{
    var a int = 1
    return &a
}
func main(){
    escape1()
}

2.栈空间不足

package main

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

3.变量大小不确定

package main

func escape3() {
	number := 10
	s := make([]int, number)
	for i := 0; i < len(s); i++ {
		s[i] = i
	}
}
func main() {
	escape3()
}

 编译期无法确定slice的长度,这种情况为了保证内存的安全,编译期也会触发逃逸,在堆上进行内存分配

直接s:=make([]int,10)不会发生逃逸

4.动态类型

动态类型就是编译期间不确定参数的类型、参数的长度也不确定的情况下就会发生逃逸

空接口interface{}可以表示任意的类型,如果函数参数为interface{},编译期间很难确定其参数的具体类型,也会发生逃逸

package main

import "fmt"

func escape4() {
	fmt.Println(1111)
}
func main() {
	escape4()
}

5.闭包引用对象

闭包函数中局部变量i在后续函数是继续使用的,编译器将其分配到堆上

package main

func escape5() func() int {
	var i int = 1
	return func() int {
		i++
		return i
	}
}
func main() {
	escape5()
}

总结:

1.栈上分配内存比堆中分配内存效率更高

2.栈上分配的内存不需要GC处理,而堆需要

3.逃逸分析目的是决定内存分配地址是栈还是堆

4.逃逸分析在编译阶段完成

无论变量大小,只要是指针变量都会在堆上分配,所以对于小变量使用传值(而不是传指针)效率更高

参考:GOLANG-ROADMAP

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Go语言中,内存逃逸指的是当一个对象的指针被多个方法或线程引用时,这个指针会逃逸到堆上。内存逃逸的位置由编译器决定,而不像C或C++可以使用malloc或new在堆上分配内存。根据内存分配的基本原则,当函数外部对指针没有引用时,优先分配在栈上;当函数外部对指针存在引用时,优先分配在堆上;当函数内部分配一个较大对象时,优先分配在堆上。\[1\] 在Go语言中,内存逃逸分析可以通过一些规则来判断。例如,当使用等号=赋值时,如果Data和Field都是引用类型的数据,则会导致Value逃逸。另外,一些特定的数据类型也会导致逃逸,比如\[\]interface{}、map\[string\]interface{}、map\[interface{}\]interface{}、map\[string\]\[\]string、map\[string\]*int、\[\]*int、func(*int)、func(\[\]string)、chan \[\]string等。具体的规则可以参考引用\[2\]中的示例。\[2\] 此外,栈空间不足也可能导致内存逃逸。当在函数中创建一个较大的切片或数组,并且栈空间不足以容纳它们时,这些切片或数组会逃逸到堆上。例如,在一个函数中创建一个长度为10000的切片,如果栈空间不足,这个切片就会逃逸到堆上。\[3\] 总结来说,内存逃逸是指当一个对象的指针被多个方法或线程引用时,这个指针会逃逸到堆上。在Go语言中,内存逃逸的位置由编译器决定,可以通过一些规则和栈空间的判断来进行分析。 #### 引用[.reference_title] - *1* [golang内存逃逸分析](https://blog.csdn.net/qq_42170897/article/details/127770234)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [golang内存逃逸](https://blog.csdn.net/wanghao3616/article/details/107284523)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [golang——内存逃逸机制](https://blog.csdn.net/weixin_45627369/article/details/127163797)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值