goalng map delete操作不会释放底层内存

golang我想删除一个map的key的时候, 只是做了个标记, 底层内存不会真正的释放, 这样可能会导致内存一直增长下去造成问题
请问有什么办法可以解决map删除某个key的时候, 底层内存也清楚了嘛

阅读 1.2k
评论
    3 个回答

    go version go1.13.1 darwin/amd64

    尝试做一次实验:

    package main
    
    import (
        "fmt"
        "runtime"
    )
    
    //var a = make(map[int]struct{})
    
    func main() {
        v := struct{}{}
    
        a := make(map[int]struct{})
    
        for i := 0; i < 10000; i++ {
            a[i] = v
        }
    
        runtime.GC()
        printMemStats("添加1万个键值对后")
        fmt.Println("删除前Map长度:", len(a))
    
        for i := 0; i < 10000-1; i++ {
            delete(a, i)
        }
        fmt.Println("删除后Map长度:", len(a))
    
        // 再次进行手动GC回收
        runtime.GC()
        printMemStats("删除1万个键值对后")
    
        // 设置为nil进行回收
        a = nil
        runtime.GC()
        printMemStats("设置为nil后")
    }
    
    func printMemStats(mag string) {
        var m runtime.MemStats
        runtime.ReadMemStats(&m)
        fmt.Printf("%v:分配的内存 = %vKB, GC的次数 = %v\n", mag, m.Alloc/1024, m.NumGC)
    }
    

    输出:

    添加1万个键值对后:分配的内存 = 241KB, GC的次数 = 1
    删除前Map长度: 10000
    删除后Map长度: 1
    删除1万个键值对后:分配的内存 = 65KB, GC的次数 = 2
    设置为nil后:分配的内存 = 65KB, GC的次数 = 3
    

    针对:底层内存不会真正的释放, 这样可能会导致内存一直增长下去造成问题。

    可以看到,新版本的 Golang 难道真的会回收 map 的多余空间哦,难道哈希表会随着 map 里面的元素变少,然后缩小了?

    我又尝试了一下,将 map 放在外层:

    package main
    
    import (
        "fmt"
        "runtime"
    )
    
    var a = make(map[int]struct{})
    
    func main() {
        v := struct{}{}
    
        //a := make(map[int]struct{})
    
        for i := 0; i < 10000; i++ {
            a[i] = v
        }
    
        runtime.GC()
        printMemStats("添加1万个键值对后")
        fmt.Println("删除前Map长度:", len(a))
    
        for i := 0; i < 10000-1; i++ {
            delete(a, i)
        }
        fmt.Println("删除后Map长度:", len(a))
    
        // 再次进行手动GC回收
        runtime.GC()
        printMemStats("删除1万个键值对后")
    
        // 设置为nil进行回收
        a = nil
        runtime.GC()
        printMemStats("设置为nil后")
    }
    
    func printMemStats(mag string) {
        var m runtime.MemStats
        runtime.ReadMemStats(&m)
        fmt.Printf("%v:分配的内存 = %vKB, GC的次数 = %v\n", mag, m.Alloc/1024, m.NumGC)
    }
    

    输出:

    添加1万个键值对后:分配的内存 = 243KB, GC的次数 = 1
    删除前Map长度: 10000
    删除后Map长度: 1
    删除1万个键值对后:分配的内存 = 244KB, GC的次数 = 2
    设置为nil后:分配的内存 = 67KB, GC的次数 = 3

    这时 map 好像内存没变化,直到设置为 nil。

    为什么全局变量就会不变呢?

    而为什么,局部变量还在使用着,它里面还剩一个元素,为什么就会缩小呢,大家都是 map,空间会一直增长,局部变量有优先权变小?难道 Golang 底层做了一些特殊处理?

    于是我又做了一次操作,将局部变量添加一万个数,然后再删除9999个数,再添加9999个,看其变化:

    package main
    
    import (
        "fmt"
        "runtime"
    )
    
    //var a = make(map[int]struct{})
    
    func main() {
        v := struct{}{}
    
        a := make(map[int]struct{})
    
        for i := 0; i < 10000; i++ {
            a[i] = v
        }
    
        runtime.GC()
        printMemStats("添加1万个键值对后")
        fmt.Println("删除前Map长度:", len(a))
    
        for i := 0; i < 10000-1; i++ {
            delete(a, i)
        }
        fmt.Println("删除后Map长度:", len(a))
    
        // 再次进行手动GC回收
        runtime.GC()
        printMemStats("删除1万个键值对后")
    
        for i := 0; i < 10000-1; i++ {
            a[i] = v
        }
    
        // 再次进行手动GC回收
        runtime.GC()
        printMemStats("再一次添加1万个键值对后")
    
        // 设置为nil进行回收
        a = nil
        runtime.GC()
        printMemStats("设置为nil后")
    }
    
    func printMemStats(mag string) {
        var m runtime.MemStats
        runtime.ReadMemStats(&m)
        fmt.Printf("%v:分配的内存 = %vKB, GC的次数 = %v\n", mag, m.Alloc/1024, m.NumGC)
    }
    

    输出:

    添加1万个键值对后:分配的内存 = 242KB, GC的次数 = 1
    删除前Map长度: 10000
    删除后Map长度: 1
    删除1万个键值对后:分配的内存 = 243KB, GC的次数 = 2
    再一次添加1万个键值对后:分配的内存 = 65KB, GC的次数 = 3
    设置为nil后:分配的内存 = 65KB, GC的次数 = 4
    

    这次局部变量删除后,和全局变量map一样了,内存耶没变化。

    但是添加10000个数后内存反而变小了。

    这神奇的 Golang 啊。

    map删除元素后map内存是不会释放的,无论是局部还是全局,但引出了上面一个奇怪的问题。

    https://github.com/golang/go/...

    为什么添加10000个数后内存反而变小了?因为 Golang 编译器有提前优化功能,它知道后面 map a 已经不会被使用了,所以会垃圾回收掉,a = nil 不起作用。

      • 0
      • 新人请关照

        map就相当于用几组固定长度数组组成的,只会删一整组,不会单个删了就删那一块的内存
        删除之后会进行装载因子的计算,大于6.5就扩容,小于就删桶

          撰写回答

          登录后参与交流、获取后续更新提醒

          相似问题
          推荐文章