Sobes.tech
Middle+

How many bytes does a map occupy in Golang?

sobes.tech AI

Answer from AI

The size of a map in Golang is not fixed and depends on many factors:

  • Number of elements: The more elements, the more memory is needed to store them.
  • Size of keys and values: Each key and each value occupy memory. Strings, slices, structures — all of these affect the overall size.
  • Metadata of the hmap structure: A map is a pointer to an hmap structure. This structure contains metadata fields:
    • element count
    • pointers to buckets
    • migration counters (grow/shrink)
    • and other metadata
  • Bucket size: Elements are stored in buckets. Each bucket has a fixed size (usually 8 key-value pairs), but the data of keys and values are stored separately, pointed to by pointers from the bucket. Buckets may contain unused space.
  • Fill density: When adding elements, the map may rehash and increase the number of buckets, which requires allocating new memory.
  • Memory alignment: Go aligns data in memory, which can lead to additional bytes for proper access.

Thus, it is impossible to specify an exact number of bytes, as it dynamically changes depending on the content and growth of the map. One can estimate a lower bound (memory for hmap and the first bucket) and an upper bound (sum of sizes of keys, values, buckets, and metadata), but the exact size is determined at runtime by Go.

To estimate size, you can use the unsafe package or debugging tools, but they will give the size at a specific moment for specific content.

// Example of an `hmap` structure (simplified)
// The structure is not intended for direct use
// and its fields may change between Go versions.
type hmap struct {
	// Note: the format of the hmap is described in ../runtime/map.go.
	// It is a hash table with buckets allocated out of the go heap.
	// hmap.buckets points to the slice of buckets (can be nil).
	// Each bucket is an array of hmap.B entries, where B is the bucket size.
	// A bucket entry stores the key and value for a single map entry,
	// plus a tophash byte.  Keys and values are stored in the same
	// bucket entry, with the values following the keys.
	// If key/value sizes are large, they are written indirectly into
	// overflow buckets, starting via overflow pointers in the main buckets.

	count     int // # live cells; strongly != 0 if map has any entries
	flags     uint8
	B         uint8  // log_2 of # buckets (can hold up to 2^B entries)
	noverflow uint16 // approximate number of overflow buckets
	hash0     uint32 // hash seed

	buckets    unsafe.Pointer // array of 2^B Buckets, allocated on heap
	oldbuckets unsafe.Pointer // previous array of 2^B Buckets, allocated on heap

	nevacuate uintptr // progress counter for evacuation (buckets moved to new)

	// true when key or value type has pointers that need scanning
	keysize  uint8 // size of key type
	valuesize uint8 // size of value type
	elemsize  uint16 // size of key + value (for in-place storage)

	// ptr to a slice of overflow buckets, allocated on heap
	overflow *[]*bmap
}

// Example of a `bmap` structure (simplified)
// Bucket type.
type bmap struct {
	// The layout of a bucket is complex.
	// See src/runtime/hashmap.go for details.
	tophash [8]uint8 // 8 tophash bytes (one per kv pair)
	// followed by keys and then values
	// followed by an overflow pointer
}