golang 切片中的内存泄漏
Posted
技术标签:
【中文标题】golang 切片中的内存泄漏【英文标题】:Memory leak in golang slice 【发布时间】:2019-07-29 10:25:58 【问题描述】:我刚开始学习围棋,在学习切片技巧时,有几点非常令人困惑。谁能帮我澄清一下。
在给定的切片中切割元素
方法一:
a = append(a[:i], a[j:]...)
但有一个注意事项,如果使用指针可能会导致内存泄漏,推荐的方法是
方法二:
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 = []BooksBook1, Book2
var bkprtSlice = []*Books&Book1, &Book2
现在做
bkSlice = bkSlice[:1]
bkSlice 仍然将 Book2 保存在支持数组中,该支持数组仍在内存中,并且不需要。 我们需要这样做吗
bkSlice[1] = Books
这样它就会被 GCed。我知道指针必须是 nil-ed,因为切片将包含对支持数组之外的对象的不必要引用。
【问题讨论】:
这不是传统意义上的内存泄漏:一旦后备数组因为没有切片引用而被垃圾收集,所有内存都被占用,并且不会发生泄漏。只是只有在所有切片都消失后才声称内存。使用推荐的代码,可以更早地声明指向的内存。 见:Does go garbage collect parts of slices? @icza 我看到了这个答案,即使是说如果我们使用指针它可能会泄漏内存..我的理解是即使我们使用直接对象也会导致内存问题。为什么它会专门针对指针。 @Volker 我同意你的观点,但为什么在指针的情况下特别提到它。它应该是天气它的指针的情况吗? 再次:永远不会有任何内存泄漏。这是关于允许更早地声明内存。这个早期的声明可以是切片包含指针(或切片或映射或通道),可以在切片之前在后备数组中被 nil-ed。如果您有一个整数切片,则无法对整数进行 GC,因为它们是后备数组的一部分。 【参考方案1】:最简单的可以通过一个简单的切片表达式来演示。
让我们从*int
指针的切片开始:
s := []*intnew(int), new(int)
这个 slice 有一个长度为 2 的后备数组,它包含 2 个非nil
指针,指向分配的整数(在后备数组之外)。
现在如果我们重新切片这个切片:
s = s[:1]
长度将变为1
。后备数组(包含 2 个指针)没有被触及,它仍然包含 2 个有效指针。即使我们现在不使用第二个指针,因为它在内存中(它是支持数组),指向的对象(这是一个用于存储int
值的内存空间)不能被垃圾收集器释放。
如果从中间“剪切”多个元素,也会发生同样的事情。如果原始切片(及其支持数组)填充了非nil
指针,并且如果您不将它们归零(使用nil
),它们将保存在内存中。
为什么这不是非指针的问题?
实际上,这是所有指针和“标题”类型(如切片和字符串)的问题,而不仅仅是指针。
如果你有一个 []int
类型的切片而不是 []*int
,那么切片它只会“隐藏”int
类型的元素,无论是否有一个切片是否包含它。 元素不是对存储在数组外的对象的引用,而指针是指在数组外的对象。
如果切片包含指针并且你在切片操作之前nil
它们,如果没有其他对指向对象的引用(如果数组是唯一持有指针的对象),它们可以被释放,它们不会由于仍然有切片(因此还有后备数组)而被保留。
更新:
当你有一片结构时:
var bkSlice = []BooksBook1, Book2
如果你像这样切片:
bkSlice = bkSlice[:1]
Book2
将无法通过bkSlice
访问,但仍会在内存中(作为后备数组的一部分)。
你不能nil
它,因为nil
不是结构的有效值。但是,您可以像这样将其 zero value 分配给它:
bkSlice[1] = Book
bkSlice = bkSlice[:1]
请注意,Books
struct 值仍将在内存中,作为后备数组的第二个元素,但该结构将是零值,因此不会保存字符串引用,因此是原书作者和标题字符串可以被垃圾收集(如果没有其他人引用它们;更准确地说是从字符串头引用的字节片)。
一般规则是“递归”:您只需要将引用位于后备数组之外的内存的元素归零。因此,如果您有一个结构切片,只有例如int
字段,您不需要将其归零,实际上这只是不必要的额外工作。如果结构具有指针或切片的字段,或者例如其他具有指针或切片等的结构类型,则应将其归零以删除对后备数组外部内存的引用。
【讨论】:
谢谢您的回答,我已经更新了问题,请您检查一次。以上是关于golang 切片中的内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章