当数组在Golang中设置为nil时,gc会收集对象吗?

当数组在Golang中设置为nil时,gc会收集对象吗?,go,memory-management,garbage-collection,heap-memory,Go,Memory Management,Garbage Collection,Heap Memory,我有一个数组,可以容纳许多对象。当我将数组设置为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

我有一个数组,可以容纳许多对象。当我将数组设置为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信息都显示76->76->76MB,这意味着gc不会释放内存。 然后,我在slash语句中添加for循环代码,第一个gc信息显示76->76->0 MB,第二个gc信息显示0->0->0 MB。
所以我很困惑,当我设置bs=nil时,没有指向所有对象的指针,为什么gc不释放这些对象?是否所有对象都应显式设置为nil?

如果在启用转义分析的情况下编译,您将看到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认为是活动的,因为它位于堆栈上。如果您将代码下推到它自己的func中,然后在它返回后使用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)

如果在启用转义分析的情况下进行编译,您将看到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认为是活动的,因为它位于堆栈上。如果您将代码下推到它自己的func中,然后在它返回后使用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)

在Go中,不需要担心GC。如果某件东西无法到达,它将被收集。这些类型的GC测试只是揭示了可能根本不存在任何堆alloc。这里没有什么可学的。GC可以工作,你可以依赖它。而且,这是一个切片,而不是一个数组。在Go中,不需要担心GC。如果某件东西无法到达,它将被收集。这些类型的GC测试只是揭示了可能根本不存在任何堆alloc。这里没有什么可学的。GC可以工作,你可以依赖它。另外,这是一个切片,而不是一个数组。从这个答案中学习的一个好东西是如何编写代码来最小化你的分配,特别是如果你分配了太多的内存,这应该小心处理。感谢@superfell提供的洞察。由于位于堆栈上,gc认为是实时的-gc仅在堆上操作。GC不考虑堆栈上的任何东西-活的、死的或其他的。堆栈被GC认为是根,参见注释,这就是为什么堆栈中分配的指向堆栈的东西不是在内部的GC。从这个答案中学习一个好的东西是如何编写代码来最小化您的分配。特别是如果你分配了太多的内存,这应该小心处理。感谢@superfell提供的洞察。由于位于堆栈上,gc认为是实时的-gc仅在堆上操作。GC不考虑堆栈上的任何东西-活的、死的或其他的。堆栈被GC认为是根,参见注释,这就是为什么堆分配指向堆栈中的东西在GC内部没有GC的原因。