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

发布于 2022-09-12 02:11:11 字数 111 浏览 28 评论 0

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

别把无礼当个性 2022-09-19 02:11:11

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 不起作用。

流殇 2022-09-19 02:11:11

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文