golang切片的扩容问题

17 阅读1分钟
package main

import "fmt"

func main() {
	var s []int
	printSlice(s)

	// 可在空切片上追加
	s = append(s, 0)
	printSlice(s)

	// 这个切片会按需增长
	s = append(s, 1)
	printSlice(s)

	// 可以一次性添加多个元素
	s = append(s, 2, 3, 4)
	printSlice(s)
}

func printSlice(s []int) {
	fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

分析打印的结果

len=0 cap=0 []

len=1 cap=1 [0]

len=2 cap=2 [0 1]

len=5 cap=6 [0 1 2 3 4]

为何最后一个打印cap=6而不是5的原因是?

// Go 的扩容策略:

// 1. 如果新长度 > 2 * 旧容量,则新容量 = 新长度

// 2. 否则:

// - 如果旧容量 < 256,则新容量 = 2 * 旧容量

// - 如果旧容量 >= 256,则新容量 = 旧容量 * 1.25 + 192

按照策略 5 > 2 * 2

应该是5,为何这里是6? 主要是因为内存对齐问题

理论容量计算:5

但 Go 会将容量向上对齐到内存分配类

int 类型占 8 字节(64位系统) 需要分配:5 * 8 = 40 字节

Go 的内存分配器使用预定义的 size class 常见的 size class(字节): 8, 16, 24, 32, 48, 64, 80, 96, ...

40 字节会被向上对齐到 48 字节

48 / 8 = 6 个 int

所以最终 cap = 6