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)
垃圾收集器还会捡起来吗?
提前致谢
不会,从地图中删除时不会出现任何内存泄漏。
对于切片,由于切片实际上使用底层数组,因此只要切片存在 - 即使它只使用该数组中的一个槽 - 数组内的指针项就无法被垃圾回收。
“切片描述了数组的一部分”,这意味着数组需要存在才能使切片存在并且不能被 GC 收集;只要某些代码指向切片。
尽管未在任何地方进行记录,但请检查源:runtime/hashmap.go
,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 }
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()
调用已将指针从映射中删除,因为(由于我们没有其他引用,所以)该指针可以进行垃圾回收了。