Go's map does not shrink

Go's map does not shrink (in terms of allocated memory). Sorry, but it just doesn't.

And the new SwissTable based map in Go 1.24 does not shrink either.

type Client struct {
    id   uint64
    body [40]byte
}

func main() {
    printAlloc("initial")

    m := make(map[int]Client)
    for i := range 10000 {
        m[i] = Client{id: uint64(i)}
    }

    runtime.GC()
    printAlloc("after create")

    for i := range 10000 {
        delete(m, i)
    }

    runtime.GC()
    printAlloc("after delete")

    runtime.KeepAlive(m)
}
initial: heap size = 42 KB
after create: heap size = 1067 KB
after delete: heap size = 1067 KB
What is printAlloc
// printAlloc prints the current heap size in KB.
func printAlloc(prefix string) uint64 {
    var m runtime.MemStats
    runtime.ReadMemStats(&m)
    alloc := m.Alloc / 1024
    fmt.Printf("%s: heap size = %d KB\n", prefix, alloc)
    return alloc
}

Of course, you can partially reclaim the memory by using pointers. GC will then free the deleted items, but the internal map structures will remain intact.

type Client struct {
    id   uint64
    body [40]byte
}

func main() {
    printAlloc("initial")

    m := make(map[int]*Client)
    for i := range 10000 {
        m[i] = &Client{id: uint64(i)}
    }

    runtime.GC()
    printAlloc("after create")

    for i := range 10000 {
        delete(m, i)
    }

    runtime.GC()
    printAlloc("after delete")

    runtime.KeepAlive(m)
}
initial: heap size = 42 KB
after create: heap size = 800 KB
after delete: heap size = 331 KB

And if the values are large enough (>128B), Go will store pointers instead.

type Client struct {
    id   uint64
    body [1024]byte
}

func main() {
    printAlloc("initial")

    m := make(map[int]Client)
    for i := range 10000 {
        m[i] = Client{id: uint64(i)}
    }

    runtime.GC()
    printAlloc("after create")

    for i := range 10000 {
        delete(m, i)
    }

    runtime.GC()
    printAlloc("after delete")

    runtime.KeepAlive(m)
}
initial: heap size = 42 KB
after create: heap size = 11581 KB
after delete: heap size = 331 KB

So the ever-growing map is usually not a big deal. But still worth knowing.

──

P.S. Want to learn more about Go? Check out my book on concurrency

★ Subscribe to keep up with new posts.