学逆向论坛

找回密码
立即注册

只需一步,快速开始

发新帖

48

积分

0

好友

1

主题

[转载图文] Go的内存逃逸

发表于 2022-3-29 17:47:17 | 查看: 1299| 回复: 2
本帖最后由 974182764@qq.co 于 2022-3-29 17:53 编辑

什么是内存逃逸
在程序中,每个函数块都会有自己的内存区域用来存自己的局部变量(内存占用少)、返回地址、返回值之类的数据,这一块内存区域有特定的结构和寻址方式,寻址起来十分迅速,开销很少。这一块内存地址称为栈。栈是线程级别的,大小在创建的时候已经确定,当变量太大的时候,会"逃逸"到堆上,这种现象称为内存逃逸。简单来说,局部变量通过堆分配和回收,就叫内存逃逸。

内存逃逸的危害
堆是一块没有特定结构,也没有固定大小的内存区域,可以根据需要进行调整。全局变量,内存占用较大的局部变量,函数调用结束后不能立刻回收的局部变量都会存在堆里面。变量在堆上的分配和回收都比在栈上开销大的多。对于 go 这种带 GC 的语言来说,会增加 gc 压力,同时也容易造成内存碎片。

如何分析程序是否发生内存逃逸
build时添加-gcflags=-m 选项可分析内存逃逸情况,比如输出./main.go:3:6: moved to heap: x 表示局部变量x逃逸到了堆上。

内存逃逸发生时机
向 channel 发送指针数据。因为在编译时,不知道channel中的数据会被哪个 goroutine 接收,因此编译器没法知道变量什么时候才会被释放,因此只能放入堆中。

package main
func main() {
    ch := make(chan int, 1)
    x := 5
    ch <- x  // x不发生逃逸,因为只是复制的值
    ch1 := make(chan *int, 1)
    y := 5
    py := &y
    ch1 <- py  // y逃逸,因为y地址传入了chan中,编译时无法确定什么时候会被接收,所以也无法在函数返回后回收y
}

局部变量在函数调用结束后还被其他地方使用,比如函数返回局部变量指针或闭包中引用包外的值。因为变量的生命周期可能会超过函数周期,因此只能放入堆中。

package main

func Foo () func (){
    x := 5            // x发生逃逸,因为在Foo调用完成后,被闭包函数用到,还不能回收,只能放到堆上存放
    return func () {
        x += 1
    }
}
func main() {
    inner := Foo()
    inner()
}

在 slice 或 map 中存储指针。比如 []*string,其后面的数组可能是在栈上分配的,但其引用的值还是在堆上。

package main
func main() {
    var x int
    x = 10
    var ls []*int
    ls = append(ls, &x)        // x发生逃逸,ls存储的是指针,所以ls底层的数组虽然在栈存储,但x本身却是逃逸到堆上
}

切片扩容后长度太大,导致栈空间不足,逃逸到堆上。

package main

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

在 interface 类型上调用方法。 在 interface 类型上调用方法时会把interface变量使用堆分配, 因为方法的真正实现只能在运行时知道。

package main
type foo interface {
    fooFunc()
}
type foo1 struct{}
func (f1 foo1) fooFunc() {}
func main() {
    var f foo
    f = foo1{}
    f.fooFunc()   // 调用方法时,f发生逃逸,因为方法是动态分配的
}

避免内存逃逸的办法
对于小型的数据,使用传值而不是传指针,避免内存逃逸。
避免使用长度不固定的slice切片,在编译期无法确定切片长度,只能将切片使用堆分配。
interface调用方法会发生内存逃逸,在热点代码片段,谨慎使用。

温馨提示:
1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
2.回复帖子不仅是对作者的认可,还可以获得学币奖励,请尊重他人的劳动成果,拒绝做伸手党!
3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【投诉建议】板块发帖举报。
已有 1 人评分学币 理由
roger + 1 感谢您的作品,赞一个!

总评分: 学币 + 1   查看全部评分

测试一下啊

    发表于 2022-3-30 21:53:46
    支持学逆向论坛,资源不错!

      发表于 2022-3-31 09:08:38
      发表于 昨天 21:53
      支持学逆向论坛

      小黑屋|手机版|站务邮箱|学逆向论坛 ( 粤ICP备2021023307号 )|网站地图

      GMT+8, 2024-4-16 12:14 , Processed in 0.099547 second(s), 50 queries .

      Powered by Discuz! X3.4

      Copyright © 2001-2021, Tencent Cloud.

      快速回复 返回顶部 返回列表