4

I'm want to understand what happens when created an empty slice with make([]int, 0). I do this code for test:

emptySlice := make([]int, 0)
fmt.Println(len(emptySlice))
fmt.Println(cap(emptySlice))
fmt.Println(unsafe.Sizeof(emptySlice))

The size and capacity return is obvious, both are 0, but the size of slice is 24 bytes, why?

24 bytes should be 3 int64 right? One internal array for a slice with 24 bytes should be something like: [3]int{}, then why one empty slice have 24 bytes?

phuclv
  • 37,963
  • 15
  • 156
  • 475
Vinicius Gabriel
  • 239
  • 1
  • 14

2 Answers2

12

If you read the documentation for unsafe.Sizeof, it explains what's going on here:

The size does not include any memory possibly referenced by x. For instance, if x is a slice, Sizeof returns the size of the slice descriptor, not the size of the memory referenced by the slice.

All data types in Go are statically sized. Even though slices have a dynamic number of elements, that cannot be reflected in the size of the data type, because then it would not be static.

The "slice descriptor", as implied by the name, is all the data that describes a slice. That is what is actually stored in a slice variable.

Slices in Go have 3 attributes: The underlying array (memory address), the length of the slice (memory offset), and the capacity of the slice (memory offset). In a 64-bit application, memory addresses and offsets tend to be stored in 64-bit (8-byte) values. That is why you see a size of 24 (= 3 * 8 ) bytes.

Hymns For Disco
  • 7,530
  • 2
  • 17
  • 33
6

unsafe.Sizeof is the size of the object in memory, exactly the same as sizeof in C and C++. See How to get memory size of variable?

A slice has size, but also has the ability to resize, so the maximum resizing ability must also be stored somewhere. But being resizable also means that it can't be a static array but needs to store a pointer to some other (possibly dynamically allocated) array

The whole thing means it needs to store its { begin, end, last valid index } or { begin, size, capacity }. That's a tuple of 3 values which means its in-memory representation is at least 3×8 bytes on 64-bit platforms, unless you want to limit the maximum size and capacity to much smaller than 264 bytes

It's exactly the same situation in many C++ types with the same dynamic sizing capability like std::string or std::vector is also a 24-byte type although on some implementations 8 bytes of padding is added for alignment reasons, resulting in a 32-byte string type. See

In fact golang's strings.Builder which is the closest to C++'s std::string has size of 32 bytes. See demo

phuclv
  • 37,963
  • 15
  • 156
  • 475