【golang学习】从源码看go 1.20的切片扩容机制

文章详细分析了Go语言中切片扩容的机制,通过源码解读发现切片扩容并非简单的固定比例增长,而是根据原始容量和新增元素数量,结合元素类型进行计算,并涉及到内存对齐等优化策略。扩容过程分为两个阶段,首先是数字计算,然后根据元素类型和内存管理进行调整。文章还探讨了为何Go语言需要如此复杂的扩容机制,以提高程序性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 问题所在

对于切片扩容机制,我最初的理解就是以下:

如果切片的容量小于1024,则扩为原来容量的两倍,如果切片容量超过1024,则扩为原来的1.25倍。
于是,我写出以下代码准备验证一下这件事:

func main() {
   
	a := make([]int, 3)		//a为3
	print("a before append:")
	slicePrint(a)
	a = append(a, 1)
	print("a after append:")
	slicePrint(a)
	b := make([]int, 100)	//b为100
	print("b before append:")
	slicePrint(b)
	b = append(b, 1)
	print("b after append:")
	slicePrint(b)
	c := make([]int, 1000)	//c为1000
	print("c before append:")
	slicePrint(c)
	c = append(c, 1)
	print("c after append:")
	slicePrint(c)
}

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

输出的结果为:

a before append:len=3 cap=3 
a after append:len=4 cap=6
b before append:len=100 cap=100
b after append:len=101 cap=224
c before append:len=1000 cap=1000
c after append:len=1001 cap=1536

可以看到的是,a切片的cap确实是变成了2倍,而b、c切片并没有按照期望那样去扩充为2倍或者是1.25倍,故可以推断出在go 1.20版本下,开头那段盛行的话是错误的。于是,我们从源码去看看到底是怎么扩容的。

2 源码分析

根据网上的信息,append()函数在扩容时调用的是…/src/runtime/slice.go文件中的growslice()函数,那么我们来看一下这个函数。这个函数很长,我们分为几个部分来看。

2.1 注释部分

在这里,我截取了注释中的参数和返回值部分,做一个简单说明。

// growslice allocates new backing store for a slice.
//
// arguments:
//
//	oldPtr = pointer to the slice's backing array
//	newLen = new length (= oldLen + num)
//	oldCap = original slice's capacity.
//	   num = number of elements being added
//	    et = element type
//
// return values:
//
//	newPtr = pointer to the new backing store
//	newLen = same value as the argument
//	newCap = capacity of the new backing store

可以看到的是,函数的参数有5个,分别是指针类型的oldPtr,指向原切片的数据地址;int类型的newLen,oldCap以及num,分别代表着新的切片长度,原切片的容量以及加入切片的元素个数;最后是一个类型的指针et,这个是切片中元素的数据类型,例如int类型与int32类型会有区别。函数的返回值有3个,分别是newPtr即新的地址,newLen新的长度,newCap新的容量。

另外,也可以从剩下的注释中得到一些其他的信息,所以源码中的注释有必要看一下。

2.2growSlice()函数主体部分

以下是go 1.20版本中的完整的growSlice()函数

func growslice(oldPtr unsafe.Pointer, newLen, oldCap, num int, et *_type) slice {
   
	oldLen := newLen - num
	if raceenabled {
   
		callerpc := getcallerpc()
		racereadrangepc(oldPtr, uintptr(oldLen*int(et.size)), callerpc, abi.FuncPCABIInternal(growslice))
	}
	if msanenabled {
   
		msanread(oldPtr, uintptr(oldLen*int(et.size)))
	}
	if asanenabled {
   
		asanread(oldPtr, uintptr(oldLen*int(et.size)))
	}

	if newLen 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值