Golang struct size and memory optimisation.

Ankit Wadhwana
TechVerito
Published in
3 min readJan 15, 2021

--

As I came across this example by my colleague, I was pleasantly surprised as I didn’t know how the memory allocation works internally, when you instantiate a struct. But not anymore 😎

Before trying to understand how and why the size of the struct varies , let’s review some fundamentals:

  1. System’s architecture: how the size in bytes of words and the maximum alignment in memory are defined. One of the main differences between 32 and 64 bit systems is about the word size, a word in a:
    32 bit system is 4 bytes and
    64 bit system is 8bytes.
  2. Golang’s primitive type sizes: how each struct’s field needs some byte size in memory. You can check the different type sizes from the supported types here, also it contains an explanation on what the word size and architecture has to do with it.
  3. Memory allocation: how padding bytes are added for properly aligning fields in memory. Usually, types with a size equal or greater than the defined word size require to be aligned at offsets that are multiples of the word size.

Putting it all together, we have a x64 system with a word size of 8 bytes and have a struct that looks something like this:

type myStruct struct {
myBool bool // 1 byte
myFloat float64 // 8 bytes
myInt int32 // 4 bytes
}

when instantiated and checked for the size of the struct using the unsafe package sizeof function, it gives 24 as the output.

a := myStruct{}
fmt.Println(unsafe.Sizeof(a)) //24
Memory allocation for myStruct

The green space is the byte used and the red space is the padding added in order to “complete” a word in memory, so the next field can start at an offset that’s multiple of a word size. At the previous example, we see that there are 11 bytes of padding that are just wasted, and this is something which can be optimised.

To optimise the previous example, we have to rearrange the struct’s fields in order to minimise the amount of padding bytes that are required for allocating the struct. Since the myInt and myBool fields have a combined size less than a word size, they can be located right next to each other(order doesn’t matter), reducing the padding bytes required from 11 to 3; so it results in something like this:

type myStructOptimized struct {
myFloat float64 // 8 bytes
myBool bool // 1 byte
myInt int32 // 4 bytes
}

when instantiated and checked for the size of the myStructOptimized struct it gives 16 as the output. That’s 8 bytes less!

b := myStructOptimized{}
fmt.Println(unsafe.Sizeof(b)) //16
Memory allocation for myStructOptimized

As you see from the Memory allocation picture, in the second example an entire row of padding is eliminated just by reordering the struct fields. An alignment can be 1, 2, 4, or 8. A padding is the space that was used to fill in the variable to fill the alignment (basically wasted space).

Try and find out how you can add 3 more boolean fields and still keep the size as 16 bytes.

Additional References

--

--