Sam*_*dra 4 pointers copy append go slice
我刚刚开始学习go,在经历切片技巧时,有两点非常令人困惑。谁能帮我澄清一下。
在给定的切片中切割元素
方法1:
a = append(a[:i], a[j:]...)
Run Code Online (Sandbox Code Playgroud)
但是有一点需要注意的是,如果使用指针并且推荐的方法可能会导致内存泄漏
方法二:
copy(a[i:], a[j:])
for k, n := len(a)-j+i, len(a); k < n; k++ {
a[k] = nil // or the zero value of T
}
a = a[:len(a)-j+i]
Run Code Online (Sandbox Code Playgroud)
谁能帮助我了解内存泄漏是如何发生的。我知道子切片将由主数组支持。我的想法与指针无关,我们是否必须始终遵循方法2。
@icza和@Volker回答后更新。
假设您有一个结构
type Books struct {
title string
author string
}
var Book1 Books
var Book2 Books
/* book 1 specification */
Book1.title = "Go Programming"
Book1.author = "Mahesh Kumar"
Book2.title = "Go Programming"
Book2.author = "Mahesh Kumar"
var bkSlice = []Books{Book1, Book2}
var bkprtSlice = []*Books{&Book1, &Book2}
Run Code Online (Sandbox Code Playgroud)
现在正在做
bkSlice = bkSlice[:1]
Run Code Online (Sandbox Code Playgroud)
bkSlice仍将Book2保留在后备阵列中,后者仍在内存中,并且不需要。所以我们需要做
bkSlice[1] = Books{}
Run Code Online (Sandbox Code Playgroud)
以便将其GC化。我了解到指针必须为零,因为切片将保留对数组背后对象的不必要引用。
最简单的可以通过简单的切片表达式来证明。
让我们从切片*int
指针开始:
s := []*int{new(int), new(int)}
Run Code Online (Sandbox Code Playgroud)
该切片具有长度为2的后备数组,并且包含2个非nil
指针,指向已分配的整数(在后备数组之外)。
现在,如果我们对这个切片进行切片:
s = s[:1]
Run Code Online (Sandbox Code Playgroud)
长度将变为1
。后备数组(保存2个指针)没有被触摸,它仍然保存2个有效的指针。即使我们现在不使用第二个指针,由于它在内存中(它是后备数组),所以指向对象(它是用于存储int
值的存储空间)不能被垃圾收集器释放。
如果您从中间“剪切”多个元素,则会发生相同的情况。如果原始切片(及其支持的数组)被非nil
指针填充,并且您没有将它们归零(使用nil
),则它们将保留在内存中。
为什么非指针不存在此问题?
实际上,这是所有指针和“标头”类型(例如切片和字符串)的问题,而不仅仅是指针。
如果您有一个切片类型[]int
而不是[]*int
,则切片将只是“隐藏”那些int
类型的元素,这些元素必须作为后备数组的一部分保留在内存中,而不管是否有包含它的切片。元素不是对存储在数组外部的对象的引用,而指针是对在数组外部的对象的引用。
如果切片包含指针,nil
而切片操作之前是指针,则如果没有其他对指向对象的引用(如果数组是唯一保存指针的对象),则可以释放它们,因为它们仍然具有切片(以及后备阵列)。
更新:
当您拥有一部分结构时:
var bkSlice = []Books{Book1, Book2}
Run Code Online (Sandbox Code Playgroud)
如果像这样切片:
bkSlice = bkSlice[:1]
Run Code Online (Sandbox Code Playgroud)
Book2
将通过变为unreachabe bkSlice
,但仍将在内存中(作为后备阵列的一部分)。
您不能这样nil
做,因为nil
它不是结构的有效值。但是,您可以像这样为它分配零值:
bkSlice[1] = Book{}
bkSlice = bkSlice[:1]
Run Code Online (Sandbox Code Playgroud)
请注意,Books
结构值仍将作为后备数组的第二个元素在内存中,但该结构将为零值,因此将不包含字符串引用,因此可以对原始的书作者和书名字符串进行垃圾回收(如果没有其他人引用它们;更确切地说是从字符串标题引用的字节片)。
一般规则是“递归”:您只需要将引用位于后备数组外部的内存的元素置零。因此,如果您拥有仅包含例如int
字段的结构片段,则无需将其归零,实际上,这只是不必要的额外工作。如果该结构的字段是指针或切片,或者其他类型的结构具有指针或切片等,则应将其清零,以便删除对后备数组之外的内存的引用。