【问题标题】:Will gc collect the objects while the array set to nil in Golang?当数组在 Golang 中设置为 nil 时 gc 会收集对象吗?
【发布时间】:2018-11-16 20:57:16
【问题描述】:

我有一个包含许多对象的数组。当我将数组设置为nil时,gc会收集数组持有的所有对象吗?

package main

import (
    "time"
    "runtime"
)

type B struct {
    bb []int
}

func NewB() *B {
    return new(B)
}

func main()  {
    var bs = make([]*B, 10)
    for i:=0; i<10; i++ {
        bs[i] = NewB()
        bs[i].bb = make([]int, 1000000)
    }

    time.Sleep(time.Second)
    println("begin gc")
    //for i:=0; i<10; i++ {
    //  bs[i] = nil
    //}
    bs = nil
    runtime.GC()
    time.Sleep(time.Second*2)
    runtime.GC()
    time.Sleep(time.Second*2)
}

首先,我设置了bs = nil,两个gc infos都显示76-&gt;76-&gt;76 MB,这意味着gc不释放内存。 然后,我在斜杠语句中添加for循环代码,第一个gc info显示76-&gt;76-&gt;0 MB,第二个gc info显示0-&gt;0-&gt;0 MB。 所以我很困惑,当我设置bs = nil 时,没有指向所有对象的指针,为什么 gc 不释放对象?所有对象都应该显式设置为 nil 吗?

【问题讨论】:

  • 在 Go 中无需担心 GC。如果某些东西无法到达,它被收集。这些类型的 GC 测试只是表明可能根本没有任何堆分配。这里没有什么可学的。 GC 有效,你可以依赖它。
  • 另外,这是一个切片,而不是一个数组。

标签: go memory-management garbage-collection heap-memory


【解决方案1】:

如果您在打开转义分析的情况下进行编译,您会看到 bs 不会转义,因此分配在堆栈上,而不是堆上

go run -gcflags '-m -l' gc.go
# command-line-arguments
./gc.go:13:12: new(B) escapes to heap
./gc.go:20:18: make([]int, 1000000) escapes to heap
./gc.go:17:15: main make([]*B, 10) does not escape

因此,尽管您已经取消了bs,但 bs 指向的切片仍然被 gc 认为是活的,因为它在堆栈上。如果你将代码下推到它自己的函数中,然后在它返回后进行 GC,你会看到 GC 确实回收了所有内存。

func main() {
    alloc()
    runtime.GC()
    time.Sleep(time.Second * 2)
}

func alloc() {
    var bs = make([]*B, 10)
    for i := 0; i < 10; i++ {
        bs[i] = NewB()
        bs[i].bb = make([]int, 1000000)
    }
    time.Sleep(time.Second)
    println("begin gc")
    bs = nil
    runtime.GC()
}



begin gc
gc 5 @1.003s 0%: 0.003+0.052+0.021 ms clock, 0.026+0/0.036/0.055+0.17 ms cpu, 76->76->76 MB, 137 MB goal, 8 P (forced)
gc 6 @1.003s 0%: 0.001+0.037+0.018 ms clock, 0.010+0/0.036/0.023+0.15 ms cpu, 76->76->0 MB, 152 MB goal, 8 P (forced)

【讨论】:

  • 从这个答案中学到的一件好事是如何编写代码来最小化你的分配,尤其是如果你分配了太多的内存,这应该小心处理。感谢@superfell 的洞察力。
  • “由于在堆栈上而被 gc 认为是实时的” - GC 仅在堆上运行。 GC 根本不考虑堆栈中的任何内容 - 活的、死的或其他。
  • 堆栈被 gc 视为根,请参阅golang.org/src/runtime/mgc.go 中的 cmets,这就是为什么从堆栈指向的堆分配的东西没有在 alloc() 中 gc'd。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-23
  • 2012-11-10
  • 2021-08-06
  • 1970-01-01
相关资源
最近更新 更多