Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does an empty slice have 24 bytes?

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?

like image 813
Vinicius Gabriel Avatar asked Sep 14 '25 22:09

Vinicius Gabriel


2 Answers

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.

like image 190
Hymns For Disco Avatar answered Sep 17 '25 19:09

Hymns For Disco


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

  • C++ sizeof Vector is 24?
  • Why is sizeof array(type string) 24 bytes with a single space element?
  • Why is sizeof(string) == 32?
  • Why is sizeof array(type string) 24 bytes with a single space element?

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

like image 41
phuclv Avatar answered Sep 17 '25 18:09

phuclv