Golang Slice中的内存泄漏 [英] Memory leak in golang slice

查看:310
本文介绍了Golang Slice中的内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚开始学习go,在经历切片技巧时,有两点非常令人困惑.谁能帮我澄清一下.

I just started learning go, while going through slice tricks, couple of points are very confusing. can any one help me to clarify.

要在给定的切片中剪切元素

To cut elements in slice its given

方法1:

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

但是要注意的是,如果使用指针并且推荐的方法是使用,可能会导致内存泄漏

but there is a note given that it may cause to memory leaks if pointers are used and recommended way is

方法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.

Can any one help me understand how memory leaks happen. I understood sub slice will be backed by the main array. My thought is irrespective of pointer or not we have to follow approach 2 always.

在@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 still holds the Book2 in backing array which is still in memory and is not required to be. so do we need to do

bkSlice[1] = Books{}

,以便将其GC化.我知道指针必须为零,因为切片将保留对后备数组外部对象的不必要引用.

so that it will be GCed. I understood pointers have to be nil-ed as the slice will hold unnecessary references to the objects outside backing array.

推荐答案

最简单的方法可以通过简单的slice表达式来演示.

Simplest can be demonstrated by a simple slice expression.

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

Let's start with a slice of *int pointers:

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

此切片具有一个长度为2的后备数组,并且包含2个非nil指针,指向已分配的整数(在后备数组之外).

This slice has a backing array with a length of 2, and it contains 2 non-nil pointers, pointing to allocated integers (outside of the backing array).

现在,如果我们重新切片该切片:

Now if we reslice this slice:

s = s[:1]

长度将变为1.后备数组(包含2个指针)没有被触摸,它仍然包含2个有效的指针.即使我们现在不使用第二个指针,由于它在内存中(它是后备数组),所以指向对象(它是用于存储int值的存储空间)不能被垃圾收集器释放.

Length will become 1. The backing array (holding 2 pointers) is not touched, it sill holds 2 valid pointers. Even though we don't use the 2nd pointer now, since it is in memory (it is the backing array), the pointed object (which is a memory space for storing an int value) cannot be freed by the garbage collector.

如果您从中间剪切"多个元素,则会发生同样的事情.如果原始切片(及其支持数组)中填充了非nil指针,并且您未将其清零(使用nil),则它们将保留在内存中.

The same thing happens if you "cut" multiple elements from the middle. If the original slice (and its backing array) was filled with non-nil pointers, and if you don't zero them (with nil), they will be kept in memory.

为什么非指针不存在此问题?

实际上,这是所有指针和标头"类型(例如切片和字符串)(不仅仅是指针)的问题.

Actually, this is an issue with all pointer and "header" types (like slices and strings), not just pointers.

如果切片类型为[]int而不是[]*int,则切片将仅隐藏" int类型的元素,这些元素必须作为后备数组的一部分保留在内存中,无论是否有一个包含或不包含它的切片. 元素不是对存储在数组外部的对象的引用,而指针是指在数组外部的对象.

If you would have a slice of type []int instead of []*int, then slicing it will just "hide" elements that are of int type which must stay in memory as part of the backing array regardless of if there's a slice that contains it or not. The elements are not references to objects stored outside of the array, while pointers refer to objects being outside of the array.

如果切片中包含指针,而您在切片操作之前nil,则没有其他对指向对象的引用(如果数组是唯一保存指针的对象),则可以释放它们,而不会由于仍然有一个切片(因此还有后备阵列)而被保留.

If the slice contains pointers and you nil them before the slicing operation, if there are no other references to the pointed objects (if the array was the only one holding the pointers), they can be freed, they will not be kept due to still having a slice (and thus the backing array).

更新:

当您拥有一片结构时:

var bkSlice = []Books{Book1, Book2}

如果您像这样切片:

bkSlice = bkSlice[:1]

Book2将通过bkSlice变得不可访问,但仍将保留在内存中(作为后备阵列的一部分).

Book2 will become unreachabe via bkSlice, but still will be in memory (as part of the backing array).

您不能nil它,因为nil不是结构的有效值.不过,您可以像这样将其零值分配给它:

You can't nil it because nil is not a valid value for structs. You can however assign its zero value to it like this:

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

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

Note that a Books struct value will still be in memory, being the second element of the backing array, but that struct will be a zero value, and thus will not hold string references, thus the original book author and title strings can be garbage collected (if no one else references them; more precisely the byte slice referred from the string header).

一般规则是递归":您只需要将引用位于后备数组外部的内存的元素归零即可.因此,如果您有一片仅包含例如int字段,您不需要将其清零,实际上,这只是不必要的额外工作.如果struct的字段是指针,切片或例如其他具有指针或分片等的结构类型,则应将其清零,以便删除对后备数组之外的内存的引用.

The general rule is "recursive": You only need to zero elements that refer to memory located outside of the backing array. So if you have a slice of structs that only have e.g. int fields, you do not need to zero it, in fact it's just unnecessary extra work. If the struct has fields that are pointers, or slices, or e.g. other struct type that have pointers or slices etc., then you should zero it in order to remove the reference to the memory outside of the backing array.

这篇关于Golang Slice中的内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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