在Go中,删除指针映射的条目会导致内存泄漏?

时间:2016-09-08 13:36:16

标签: dictionary go memory-leaks

这里的第一个计时器,

SliceTricks中的第一个注意表示在切割或删除指针中的元素时可能存在内存泄漏问题。

地图是否一样?例如:https://play.golang.org/p/67cN0JggWY

我们是否应该在删除地图之前将条目设为零?像这样:

m["foo"] = nil

如果我们只是清除地图会怎样?

m = make(map[string]*myStruct)

垃圾收集器是否仍会接收它?

提前致谢

2 个答案:

答案 0 :(得分:5)

检查来源

虽然没有在任何地方记录,但检查来源:runtime/hashmap.gomapdelete()功能:

558 func mapdelete(t *maptype, h *hmap, key unsafe.Pointer) {
        // ...
600             memclr(k, uintptr(t.keysize))
601             v := unsafe.Pointer(uintptr(unsafe.Pointer(b)) + dataOffset + bucketCnt*uintptr(t.keysize) + i*uintptr(t.valuesize))
602             memclr(v, uintptr(t.valuesize))
        // ...
618 }

如您所见,密钥(第600行)和值(第#602行)的存储被清除/清零。

这意味着如果任何键或值是一个指针,或者如果它们是包含指针的复杂类型的值,它们将被清零,因此指向的对象不再被地图的内部数据结构引用,所以这里没有内存泄漏。

当没有更多引用完整的map值时,map的完整内存区域将被垃圾收集,并且键和值中包含的所有指针也不再被保留按地图;如果没有其他人引用指向的对象,它们将被正确地进行垃圾收集。

构建一个证明这个的例子

我们还可以构建一个测试代码,在不检查来源的情况下证明这一点:

type point struct {
    X, Y int
}

var m = map[int]*point{}

func main() {
    fillMap()
    delete(m, 1)
    runtime.GC()
    time.Sleep(time.Second)
    fmt.Println(m)
}

func fillMap() {
    p := &point{1, 2}
    runtime.SetFinalizer(p, func(p *point) {
        fmt.Printf("Finalized: %p %+v\n", p, p)
    })
    m[1] = p
    fmt.Printf("Put in map: %p %+v\n", p, p)
}

输出(在Go Playground上尝试):

Put in map: 0x1040a128 &{X:1 Y:2}
Finalized: 0x1040a128 &{X:1 Y:2}
map[]

这是做什么的?它创建一个*Point值(指向结构的指针),将其放入映射中,并注册一个函数,当该指针变得无法访问时,应该被调用(使用runtime.SetFinalizer()) ,然后删除包含此指针的条目。然后我们将runtime.GC()称为"强制"立即垃圾收集。我还在最后打印地图,以确保整个地图不会因为一些优化而被垃圾收集。

结果呢?我们看到已注册的函数被调用,这证明指针已作为delete()调用的结果从映射中删除,因为(因为我们没有其他引用它)它有资格进行垃圾收集。

答案 1 :(得分:3)

不,从地图中删除时不会有任何内存泄漏。

在切片的情况下,由于切片实际上使用了底层数组,只要切片存在 - 即使它只使用该数组中的一个插槽 - 数组中的指针项也不会被垃圾收集。

" A slice describes a piece of an array"这意味着数组需要在那里存在切片并且不能被GC收集;只要一些代码指向切片。