arrays - go 垃圾会收集部分 slice 吗?

标签 arrays go garbage-collection slice

如果我实现这样的队列...

package main

import(
    "fmt"
)

func PopFront(q *[]string) string {
    r := (*q)[0]
    *q = (*q)[1:len(*q)]
    return r
}

func PushBack(q *[]string, a string) {
    *q = append(*q, a)
}

func main() {
    q := make([]string, 0)

    PushBack(&q, "A")
    fmt.Println(q)
    PushBack(&q, "B")
    fmt.Println(q)
    PushBack(&q, "C")
    fmt.Println(q)

    PopFront(&q)
    fmt.Println(q)
    PopFront(&q)
    fmt.Println(q)      
}

...我最终得到一个数组 ["A", "B", "C"] 没有指向前两个元素的 slice 。由于 slice 的“开始”指针永远不能递减(AFAIK),因此永远不能访问这些元素。

Go 的垃圾收集器是否足够聪明以释放它们?

最佳答案

slice 只是描述符(类似结构的小型数据结构),如果不被引用,将被正确地回收。

另一方面, slice (描述符指向的)的底层数组在通过重新 slice 创建的所有 slice 之间共享:引用 Go Language Specification: Slice Types :

A slice, once initialized, is always associated with an underlying array that holds its elements. A slice therefore shares storage with its array and with other slices of the same array; by contrast, distinct arrays always represent distinct storage.

因此,如果至少存在一个 slice ,或者一个保存数组的变量(如果 slice 是通过对数组进行 slice 创建的),则不会被垃圾回收。

官方声明:

博文Go Slices: usage and internals Andrew Gerrand 明确指出了这种行为:

As mentioned earlier, re-slicing a slice doesn't make a copy of the underlying array. The full array will be kept in memory until it is no longer referenced. Occasionally this can cause the program to hold all the data in memory when only a small piece of it is needed.

...

Since the slice references the original array, as long as the slice is kept around the garbage collector can't release the array.

回到你的例子

虽然底层数组不会被释放,但请注意,如果您将新元素添加到队列中,内置的 append 函数有时可能会分配一个新数组并将当前元素复制到新的– 但是复制只会复制 slice 的元素,而不是整个底层数组!当发生这种重新分配和复制时,如果不存在其他引用,则“旧”数组可能会被垃圾回收。

另外一个很重要的事情是,如果从前面弹出一个元素, slice 将被重新 slice 并且不包含对弹出元素的引用,但是由于底层数组仍然包含该值,因此该值也将保留在内存(不仅仅是数组)。建议无论何时从队列( slice/数组)中弹出或删除元素,始终将其归零( slice 中的相应元素),这样该值就不会不必要地保留在内存中。如果您的 slice 包含指向大数据结构的指针,这将变得更加重要。

func PopFront(q *[]string) string {
    r := (*q)[0]
    (*q)[0] = ""  // Always zero the removed element!
    *q = (*q)[1:len(*q)]
    return r
}

提到了Slice Tricks wiki page:

Delete without preserving order

a[i] = a[len(a)-1] 
a = a[:len(a)-1]

NOTE If the type of the element is a pointer or a struct with pointer fields, which need to be garbage collected, the above implementations of Cut and Delete have a potential memory leak problem: some elements with values are still referenced by slice a and thus can not be collected.

关于arrays - go 垃圾会收集部分 slice 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28432658/

相关文章:

python - 何时在 PySide 中设置父级

javascript - 在 React 中根据对象属性更改类

ruby-on-rails - 用斜杠将 Ruby 数组拆分为子数组

dictionary - Go中的深度合并订单图

go - 在 Golang 中返回一个新的 Struct 实例

objective-c - 运行数小时后,垃圾收集导致 EXC_BAD_ACCESS 崩溃

python - 为什么即使禁用了 gc,python 仍在收集?

c++ - 二维动态字符数组cpp

ios - 在 Swift 中跳过空数组?

go - 发送前调整图像大小