Go

How does append grow a slice? Why is the resulting capacity sometimes not exactly double?

Question 42HardGo 1.22 to 1.25

When len + n > cap, append calls runtime.growslice. Since Go 1.20 the rule (nextslicecap) is:

  • If the needed length is more than twice the old cap, use the needed length.
  • If old cap < 256, double it.
  • Otherwise grow by (newcap + 3*256) / 4 repeatedly. The factor moves smoothly from 2x down toward 1.25x for large slices.

The byte size is then rounded up to a malloc size class, so the final cap is often larger than the formula gives.

var s []int
prev := -1
for i := range 2000 {
	s = append(s, i)
	if cap(s) != prev {
		fmt.Print(cap(s), " ")
		prev = cap(s)
	}
}
// 64-bit: 1 2 4 8 16 32 64 128 256 512 848 1280 1792 2560

x := append([]int(nil), 1, 2, 3, 4, 5)
fmt.Println(cap(x)) // 6, not 5: 40 bytes rounds up to the 48-byte size class

Gotchas: never write code that depends on exact capacities, because they depend on the Go version and element size. If you know the final size, preallocate with make([]T, 0, n) to avoid O(log n) reallocations and copies. Growth also leaves the old array for the GC, so repeated appends to large slices cause memory churn.

More on Arrays, Slices, Maps & Strings

All 37 Arrays, Slices, Maps & Strings questions