去垃圾收集切片的部分? [英] Does go garbage collect parts of slices?

查看:98
本文介绍了去垃圾收集切片的部分?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  package main 

import(
fmt


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

func PushBack(q * []字符串,字符串){
* 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] ,它没有指向前两个元素的切片。由于切片的开始指针永远不会被减少(AFAIK),因此这些元素永远不会被访问。



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

解决方案

切片只是描述符(类似于小型结构的数据结构),如果未被引用,将会被正确地垃圾收集。



另一方面,片段(描述符指向的片段)的底层数组是共享,这些片段是通过重新创建片段创建的:引用来自 Go Language Specification:Slice Types

< blockquote>

切片初始化后总是与保存其元素的基础数组关联。因此,切片与其阵列以及同一阵列的其他切片共享存储;相反,不同的数组总是表示不同的存储。


因此,如果至少只有一个切片存在或数组的变量是通过切片数组创建的,它不会被垃圾收集。



有关此的官方声明:



博客文章 切片:使用情况和内部结构 安德鲁杰兰清楚地表明了这种行为:


如前所述,重新切片不会使切片底层数组的副本。 整个数组将保存在内存中,直到它不再被引用为止。偶尔,这会导致程序只在需要的时候将所有数据保存在内存中。


$ b

...



由于片引用了原始数组,所以只要片保留在垃圾收集器周围无法释放数组。


返回您的示例



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

另外一个非常重要的事情是,如果一个元素从前面弹出,切片将被重构并且不包含对弹出元素的引用,但由于底层数组仍然包含该值,因此该值也将保留在内存中(而不仅仅是数组)。建议每当一个元素被弹出或从队列中移除(slice / array)时,总是将它归零(它在slice中的相应元素),这样这个值就不会不必要地留在内存中。如果你的slice包含指向大数据结构的指针,这变得更加关键。

  func PopFront(q * [] string)string { 
r:=(* q)[0]
(* q)[0] =//总是清零元素!
* q =(* q)[1:len(* q)]
return r
}


If I implement a queue like this...

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)      
}

... I end up with an array ["A", "B", "C"] that has no slices pointing to the first two elements. Since the "start" pointer of a slice can never be decremented (AFAIK), those elements can never be accessed.

Is Go's garbage collector smart enough to free them?

解决方案

Slices are just descriptors (small struct-like data structures) which if not referenced will be garbage collected properly.

The underlying array for a slice (to which the descriptor points to) on the other hand is shared between all slices that are created by reslicing it: quoting from the 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.

Therefore if at least only one slice exists or a variable to the array if a slice was created by slicing the array, it will not be garbage collected.

Official Statement about this:

The blog post Go Slices: usage and internals By Andrew Gerrand clearly states this behaviour:

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.

Back to your example

While the underlying array will not be freed, note that if you add new elements to the queue, the built-in append function occasionally might allocate a new array and copy the current elements to the new - but copying will only copy the elements of the slice and not the whole underlying array! When such a reallocation and copying occurs, the "old" array may be garbage collected if no other reference exists to it.

Also another very important thing is that if an element is popped from the front, the slice will be resliced and not contain a reference to the popped element, but since the underlying array still contains that value, the value will also remain in memory (not just the array). It is recommended that whenever an element is popped or removed from your queue (slice/array), always zero it (its respective element in the slice) so the value will not remain in memory needlessly. This becomes even more critical if your slice contains pointers to big data structures.

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

这篇关于去垃圾收集切片的部分?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆