在Go中,删除指针映射的条目会导致内存泄漏? [英] In Go, deleting an entry of a map of pointers causes memory leak?

查看:137
本文介绍了在Go中,删除指针映射的条目会导致内存泄漏?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

第一次计时在这里, 注 wiki / SliceTricksrel =nofollow> SliceTricks 暗示在切割或删除指针片段中的元素时可能会出现内存泄漏问题。



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



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

  m [foo] = nil 

如果我们简单地清除地图,该怎么办?

  m = make(map [string] * myStruct)

垃圾回收器是否仍会选取它提前致谢

解决方案

检查来源 h2>

虽然这没有记录在任何地方,但查看来源: 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}

正如您所看到的,清除/归零键(行#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\\\
,p,p)
})
m [1] = p
fmt.Printf(放入地图:%p%+ v \ n,p,p)
}

输出(在 Go Playground ):

 放入地图:0x1040a128& {X:1 Y:2} 
Final ized:0x1040a128& {X:1 Y:2}
map []

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



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


first timer here,

The first NOTE in SliceTricks suggests that there is a potential memory leak problem when cutting or deleting elements in a slice of pointers.

Is the same true for a map? For example: https://play.golang.org/p/67cN0JggWY

Should we nil the entry before deleting from map? Like so:

m["foo"] = nil

What if we simply clear the map?

m = make(map[string]*myStruct)

Will the garbage collector still pick it up?

Thanks in advance

解决方案

Checking the sources

Although this is not documented anywhere, checking the sources: runtime/hashmap.go, mapdelete() function:

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 }

As you can see, storage for both the key (line #600) and the value (line #602) are cleared / zeroed.

This means if any of the key or value was a pointer, or if they were values of complex types containing pointers, they are zeroed and therefore the pointed objects are no longer referenced by the internal data structures of the map, so there is no memory leak here.

When there is no more reference to a complete map value, then the complete memory area of the map will be garbage collected, and all the pointers included in keys and values are also not held anymore by the map; and if no one else has reference to the pointed objects, they will be garbage collected properly.

Constructing an example to prove this

We can also construct a test code which proves this without examining the sources:

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)
}

Output (try it on the Go Playground):

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

What does this do? It creates a *Point value (pointer to a struct), puts it in the map, and registers a function that should be called when this pointer becomes unreachable (using runtime.SetFinalizer()), and then deletes the entry containing this pointer. Then we call runtime.GC() to "force" an immediate garbage collection. I also print the map at the end just to make sure the whole map is not garbage collected due to some optimization.

The result? We see the registered function gets called, which proves the pointer was removed from the map as the result of the delete() call, because (since we had no other references to it) it was eligible for garbage collection.

这篇关于在Go中,删除指针映射的条目会导致内存泄漏?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆