golang深度理解slice扩容机制及其1.18变动

一、误区

最近面试很多候选人,在问到slice的扩容机制时,大多回答的是:当容量小于1024时,双倍扩容,大于1024时,1.25倍扩容。这种回答虽不能说完全错,但是不够准确和完整。究其原因,往往是看了网上给的一些常见回答,并没有进一步的深入理解。

这篇文章希望通过阅读源码,梳理一下slic扩容的完整流程,并说明在golang版本1.18之后所做的一些调整。

二、示例

func Test_GrowSlice(t *testing.T) {
	// int64的切片示例
	var s1 = make([]int64, 0)
	for i := 0; i < 1025; i++ {
		s1 = append(s1, 1)
	}
	fmt.Printf("slice1 len=%d, cap=%d \n", len(s1), cap(s1))
	//输出:slice1 len=1025, cap=1280 

	// int32的切片示例
	var s2 = make([]int32, 0)
	for i := 0; i < 1025; i++ {
		s2 = append(s2, 1)
	}
	fmt.Printf("slice2 len=%d, cap=%d \n", len(s2), cap(s2))
	// 输出:slice2 len=1025, cap=1344 
}

看到第一个输出应该不会觉得奇怪,与我们理解的一致,1024 * 1.25 = 1280,容量扩容为1280。

但是第二个输出就有点超出预期了,逻辑一样的代码,仅仅是把切片的元素类型修改为int32,容量怎么就变成1344了呢。接下来我们分析完源码,就可以解释了。

三、源码分析

首先,我们先看下golang库下runtime/slice.go文件,其中的growslice函数,就是我们要分析的主要逻辑。ssl 

func growslice(et *_type, old slice, cap int) slice {
	// 省略.....

	newcap := old.cap
	doublecap := newcap + newcap
	if cap > doublecap {
		newcap = cap
	} else {
		if old.cap < 1024 {
			newcap = doublecap
		} else {
			// Check 0 < newcap to detect overflow
			// and prevent an infinite loop.
			for 0 < newcap && newcap < cap {
				newcap += newcap / 4
			}
			// Set newcap to the requested cap when
			// the newcap calculation overflowed.
			if newcap <= 0 {
				newcap = cap
			}
		}
	}
    // 省略......
}

先看下growslice函数中,最开始的这段代码。 这段逻辑就是我们通常理解的:当容量小于1024时,双倍扩容,大于1024时,1.25倍扩容。好多人看到这里就浅尝辄止了,想要完整理解,我们还要接着往下看。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值