golang切片中的内存泄漏

问题描述 投票:3回答:1

我刚刚开始学习go,虽然经历了切片技巧,但有几点非常令人困惑。任何人都可以帮我澄清一下。

切割切片中的元素

方法1:

a = append(a[:i], a[j:]...)

但是有一个注释,如果使用指针并且推荐的方法是,它可能会导致内存泄漏

方法2:

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]

任何人都可以帮助我理解内存泄漏是如何发生的。据我所知,子切片将由主阵列支持。我的想法与指针无关,我们必须始终遵循方法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}

现在在做

bkSlice = bkSlice[:1]

bkSlice仍然将Book2保留在后备阵列中,该阵列仍然在内存中并且不是必需的。所以我们需要这样做

bkSlice[1] = Books{}

所以它将被GCed。我理解指针必须是nil-ed,因为切片将保持对支持数组之外的对象的不必要的引用。

pointers go copy append slice
1个回答
4
投票

最简单的可以通过简单的切片表达来证明。

让我们从一片*int指针开始:

s := []*int{new(int), new(int)}

这个切片有一个长度为2的后备数组,它包含2个非nil指针,指向分配的整数(在后备数组之外)。

现在,如果我们重新审视这个片段:

s = s[:1]

长度将成为1。没有触及支持数组(保持2个指针),它将保存2个有效指针。即使我们现在不使用第二个指针,因为它在内存中(它是后备数组),垃圾收集器无法释放指向的对象(用于存储int值的内存空间)。

如果你从中间“切割”多个元素,就会发生同样的事情。如果原始切片(及其支持数组)填充了非nil指针,并且如果不将它们归零(使用nil),它们将保留在内存中。

为什么这不是非指针的问题?

实际上,这是所有指针和“标题”类型(如切片和字符串)的问题,而不仅仅是指针。

如果你有一个[]int类型的切片而不是[]*int,那么切片它将只是“隐藏”int类型的元素,它必须作为后备数组的一部分保留在内存中,无论是否有包含它的切片。元素不是对存储在数组外部的对象的引用,而指针引用的是在数组外部的对象。

如果切片包含指针并且你在切片操作之前对它们进行了nil,如果没有对指向对象的其他引用(如果数组是唯一一个持有指针的数组),它们可以被释放,它们将不会被保留具有切片(并因此具有背衬阵列)。

更新:

当你有一块结构:

var bkSlice = []Books{Book1, Book2}

如果你把它切成:

bkSlice = bkSlice[:1]

Book2将通过bkSlice变为不可用,但仍将在内存中(作为后备阵列的一部分)。

你不能nil它因为nil不是结构的有效值。但是你可以像这样分配它的zero value

bkSlice[1] = Book{}
bkSlice = bkSlice[:1]

请注意,Books结构值仍将在内存中,作为后备数组的第二个元素,但该结构将为零值,因此不会保存字符串引用,因此原始书籍作者和标题字符串可以被垃圾收集(如果没有其他人引用它们;更确切地说是从字符串头引用的字节切片)。

一般规则是“递归”:您只需要引用位于后备阵列外部的内存的零元素。所以,如果你有一块只有例如结构的结构int字段,你不需要将它归零,事实上它只是不必要的额外工作。如果结构具有指针或切片的字段,或者例如其他具有指针或切片等的结构类型,则应将其归零以删除对支持数组外部的内存的引用。

© www.soinside.com 2019 - 2024. All rights reserved.