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

Dictionary 在Go中,删除指针映射的条目会导致内存泄漏吗?,dictionary,pointers,go,memory-leaks,Dictionary,Pointers,Go,Memory Leaks,第一次来这里 中的第一个注释表明在剪切或删除指针片中的元素时可能存在内存泄漏问题 地图也是这样吗?例如: 我们应该在从映射中删除之前将条目置零吗?像这样: m["foo"] = nil 如果我们只是清理地图呢 垃圾收集器还会把它捡起来吗 提前感谢否,从映射中删除时不会出现任何内存泄漏 对于片,由于片实际上使用一个底层数组,因此只要片存在——即使它只使用该数组中的一个插槽——数组中的指针项就不能被垃圾回收 “”这意味着数组必须存在,才能使切片存在,并且不能被GC收集;只要有代码指向切片。检查源代

第一次来这里

中的第一个注释表明在剪切或删除指针片中的元素时可能存在内存泄漏问题

地图也是这样吗?例如:

我们应该在从映射中删除之前将条目置零吗?像这样:

m["foo"] = nil
如果我们只是清理地图呢

垃圾收集器还会把它捡起来吗


提前感谢

否,从映射中删除时不会出现任何内存泄漏

对于片,由于片实际上使用一个底层数组,因此只要片存在——即使它只使用该数组中的一个插槽——数组中的指针项就不能被垃圾回收

“”这意味着数组必须存在,才能使切片存在,并且不能被GC收集;只要有代码指向切片。

检查源代码 尽管这在任何地方都没有记录,但请检查源代码:,
mapdelete()
函数:

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行)的存储都已清除/归零

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

当不再引用完整的
映射
值时,
映射
的完整内存区域将被垃圾收集,键和值中包含的所有指针也不再被映射持有;如果没有其他人引用指向的对象,它们将被正确地垃圾收集

构造一个例子来证明这一点 我们还可以构造一个测试代码,证明这一点,而无需检查源代码:

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)
}
输出(在上尝试):

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


结果如何?我们看到注册函数被调用,这证明指针是由于
delete()
调用而从映射中删除的,因为(因为我们没有其他引用)它符合垃圾收集的条件。

没有泄漏。我们不应该将条目置零。从您发布的代码片段中不清楚是什么阻止了其他地方引用的值指针被GC'd,例如,如果您在
fillMap()
调用之后的行中添加
fmt.Println(v)
,然后在删除映射条目之后添加
fmt.Println(v)
,结构按预期打印,并且未归零。这很有道理,为什么它会有用,但是从
mapdelete
代码片段中看不清楚为什么它没有归零。@ashgromnies我不确定我是否理解你想说的话。如果存在对可访问对象的任何引用,则无法对其进行gc'd。如果映射只有一个引用,并且从映射中删除了它,那么它符合gc的条件。
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)
}
Put in map: 0x1040a128 &{X:1 Y:2}
Finalized: 0x1040a128 &{X:1 Y:2}
map[]