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

cal*_*ado 2 dictionary pointers memory-leaks go

第一次来这里

第一SliceTricks表明,切割或删除在指针的切片元素时有一个潜在的内存泄漏问题。

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

从地图上删除之前,我们应否删除条目?像这样:

m["foo"] = nil
Run Code Online (Sandbox Code Playgroud)

如果我们只是清除地图怎么办?

m = make(map[string]*myStruct)
Run Code Online (Sandbox Code Playgroud)

垃圾收集器还会捡起来吗?

提前致谢

Kav*_*ian 7

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

对于切片,由于切片实际上使用底层数组,因此只要切片存在 - 即使它只使用该数组中的一个槽 - 数组内的指针项就无法被垃圾回收。

切片描述了数组的一部分”,这意味着数组需要存在才能使切片存在并且不能被 GC 收集;只要某些代码指向切片。


icz*_*cza 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 }
Run Code Online (Sandbox Code Playgroud)

如您所见,键(第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)
}
Run Code Online (Sandbox Code Playgroud)

输出(在Go Playground上尝试):

Put in map: 0x1040a128 &{X:1 Y:2}
Finalized: 0x1040a128 &{X:1 Y:2}
map[]
Run Code Online (Sandbox Code Playgroud)

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

结果?我们看到已注册的函数被调用,这证明了该delete()调用已将指针从映射中删除,因为(由于我们没有其他引用,所以)该指针可以进行垃圾回收了。