golang内存逃逸和内存分配

先说说golang中内存分配方式:

主要是堆(heap)和栈(stack)分配两种。

栈分配廉价,堆分配昂贵。

栈分配:对于栈的操作只有入栈和出栈两种指令,属于静态资源分配。

堆分配:堆中分配的空间,在结束使用之后需要垃圾回收器进行闲置空间回收,属于动态资源分配。

使用栈分配:函数的内部中不对外开放的局部变量,只作用于函数中。

使用堆分配:1.函数的内部中对外开放的局部变量。2.变量所需内存超出栈所提供最大容量。

看下面这段代码:

package main
 
import(
  "fmt"
  "runtime"
  "time"
)
 
func foo() {
    runtime.GOMAXPROCS(1)
    for i := 0; i < 10; i++ {
        go func() {
            fmt.Print(i, " ")
        }()
    }
    runtime.Gosched()
    time.Sleep(time.Second)
}
 
func main() {
  foo();
}
这里函数foo里面的i是局部变量,并且没有外部引用,不对外开放,所以是分配在栈中的。

运行命令

go build -gcflags '-m' ./gofunc.go


可以看到,i逃逸到堆中了。

那么由于i存在于堆中,并没有随着foo函数结束而释放,并且i的值为10,这就解释了为什么运行结果为10个10了:

下面是四种最常见引发内存逃逸的情况:

发送指针或带有指针的值到 channel 中。在编译时,是没有办法知道哪个 goroutine 会在 channel 上接收数据。所以编译器没法知道变量什么时候才会被释放。

在一个切片上存储指针或带指针的值。一个典型的例子就是 []*string。这会导致切片的内容逃逸。尽管其后面的数组可能是在栈上分配的,但其引用的值一定是在堆上。

slice 的背后数组被重新分配了,因为 append 时可能会超出其容量(cap)。slice 初始化的地方在编译时是可以知道的,它最开始会在栈上分配。如果切片背后的存储要基于运行时的数据进行扩充,就会在堆上分配。

在 interface 类型上调用方法。在 interface 类型上调用方法都是动态调度的 —— 方法的真正实现只能在运行时知道。想像一个 io.Reader 类型的变量 r, 调用 r.Read(b) 会使得 r 的值和切片 b 的背后存储都逃逸掉,所以会在堆上分配。

一个经验是:指针指向的数据都是在堆上分配的。

参考:https://www.jianshu.com/p/63404461e520
————————————————
版权声明:本文为CSDN博主「ReignsDu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/reigns_/article/details/102928763

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值