14

Given a map allocation where the initial space is not specified, for example:

foo := make(map[string]int)

The documentation suggests that the memory allocation here is implementation dependent. So (how) can I tell how much memory my implementation is allocating to this map?

icza
  • 389,944
  • 63
  • 907
  • 827
lash
  • 746
  • 1
  • 7
  • 23

3 Answers3

17

If you look at the source of Go's map type, you will see, that a map consists of a header (type hmap) and an array of buckets (type bmap). When you create a new map and don't specify the initial space (hint), only one bucket is created.

A header consists of several fields:

1 * int,
2 * uint8,
1 * uint16,
1 * uint32,
2 * unsafe.Pointer,
1 * uintptr.

Size of the types int, uintptr, and unsafe.Pointer equals the size of a word (8 bytes on 64 bit machines).

A bucket consists of an array of 8 * uint8.

This gives a total of 40 + 8 = 48 bytes (64 bit architecture)

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
dev.bmax
  • 8,998
  • 3
  • 30
  • 41
15

You may use the Go testing tool to measure size of arbitrary complex data structures. This is detailed in this answer: How to get variable memory size of variable in golang?

To measure the size of a map created by make(map[string]int), use the following benchmark function:

var x map[string]int

func BenchmarkEmptyMap(b *testing.B) {
    for i := 0; i < b.N; i++ {
        x = make(map[string]int)
    }
}

Executing with

go test -bench . -benchmem

The result is:

BenchmarkEmptyMap-4     20000000   110 ns/op      48 B/op    1 allocs/op

So the answer is on my 64-bit architecture: 48 bytes.

As hinted, size may depend on architecture. Also size may depend on the initial capacity you may pass to make(), as you can see in this example:

func BenchmarkEmptyMapCap100(b *testing.B) {
    for i := 0; i < b.N; i++ {
        x = make(map[string]int, 100)
    }
}

Output:

BenchmarkEmptyMapCap100-4   1000000    1783 ns/op   4176 B/op    3 allocs/op

A map of type map[string]int with an initial capacity of 100 now requires 4176 bytes (on 64-bit arch).

The default initial capacity is around 7 if not specified explicitly.

icza
  • 389,944
  • 63
  • 907
  • 827
  • If I allocate a map of size 1000, will the memory usage of the map grow even if there are less than 1000 values in the map ? I would like to determine the memory usage of a map, or at least determine an upper bound. It is to evaluate the cost in memory usage of a cache. – chmike Jun 04 '21 at 05:59
  • @chmike This answer gives you the tools to test it. Have you tried? Btw yes, memory usage and allocation might increase a little due to hashmap internals (buckets usage and allocation). But that's "tiny" compared to the memory footprint of the whole map. – icza Jun 04 '21 at 06:22
  • why is `x` a global variable though? what's the difference from putting it on the stack of `BenchmarkEmptyMap`? – SOFe May 30 '22 at 10:18
  • @SOFe I just made it a package level var to make sure the compiler doesn't optimize it away (as it's never read). – icza May 30 '22 at 10:36
0

I encountered the same requirement, and finally I did it with the help of unsafe, and you can look at MapSize, but it's worth noting that it's not entirely accurate.