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

Yaf*_*ang 1 garbage-collection memory-management heap-memory go

我有一个包含许多对象的数组。当我将数组设置为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)
}
Run Code Online (Sandbox Code Playgroud)

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

sup*_*ell 5

如果在启用转义分析的情况下进行编译,您会看到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
Run Code Online (Sandbox Code Playgroud)

因此,尽管您没有指定bsbs指向的切片,但由于位于堆栈上,因此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)
Run Code Online (Sandbox Code Playgroud)