结合内存分配机制分析Go切片扩容机制

0x00 引子

先来看一段代码:

	a := make([]int, 0, 2)
	a = append(a, 1, 2)
	fmt.Printf("a:%v, len:%d, cap:%d\n", a, len(a), cap(a)) // ①

	a = append(a, 3, 4, 5)
	fmt.Printf("a:%v, len:%d, cap:%d\n", a, len(a), cap(a)) // ②

	// 输出结果为:
	// ①   a:[1 2], len:2, cap:2
	// ②   a:[1 2 3 4 5], len:5, cap:6

为什么在②处的切片a的cap变成了6?

0x01 Go切片扩容机制

Step.1 预估扩容后的容量 newCap1

变量定义

  • oldLen :扩容前的 len
  • newLen :append 操作后切片内的元素数
  • oldCap : 扩容前的 cap
  • newCap1 :预估 扩容后的 cap
  • newCap2 :实际 扩容后的 cap

预估规则

  1. newLen > 2 * oldCap ,则 newCap1 = newLen
  2. newLen ≤ 2 * oldCap
    1. oldLen < 1024 ,则 newCap1 = oldCap * 2
    2. oldLen ≥ 1024 ,则 newCap1 = oldCap * 1.25

Step.2 结合内存分配计算 newCap2

两个核心:

  • 预估容量 newCap1 * 元素类型大小 = 内存分配需求
  • 内存分配器根据 内存分配需求 和 spanClass 规格,按块分配
  • 实际分配容量 newCap2 = 满足内存分配需求 的最小 spanClass大小 / 元素类型大小

其中,目前版本 spanClass 规格大致有:

8, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192 …

e.g.

已知:

  • newCap1 = 3
  • 元素类型大小 = 8

则:

  • 内存分配需求 = 24 (newCap1 * 元素类型大小)
  • 结合 spanClass大小 知,此时应分配的内存大小为 32
  • 则 newCaps2 = 4 (满足内存分配需求 的最小 spanClass大小 32 / 元素类型大小 8)

0x02 引子问题分析

	a := make([]int, 0, 2)
	a = append(a, 1, 2)
	fmt.Printf("a:%v, len:%d, cap:%d\n", a, len(a), cap(a)) // ①

	a = append(a, 3, 4, 5)
	fmt.Printf("a:%v, len:%d, cap:%d\n", a, len(a), cap(a)) // ②

	// 输出结果为:
	// ①   a:[1 2], len:2, cap:2
	// ②   a:[1 2 3 4 5], len:5, cap:6

Step.1 预估扩容后的容量 newCap1

命中 「 newLen > 2 * oldCap ,则 newCap1 = newLen」 规则 ,newCap1 = 5

Step.2 结合内存分配计算 newCap2

已知:

  • newCap1 = 5
  • 元素类型大小 = 8

则:

  • 内存分配需求 = 40 (newCap1 * 元素类型大小)
  • 结合 spanClass大小 知,此时应分配的内存大小为 48
  • 则 newCaps2 = 6 (满足内存分配需求 的最小 spanClass大小 48 / 元素类型大小 8)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值