golang的切片扩容机制

golang的切片扩容机制是golang面试者绕不开的一扇大门,无论在面试提问,或者面试情景上都绕不开它,今天就说说我理解下的切片扩容。

golang的扩容机制:在go1.18之前有一个临界值为1024,小于1024的时候,切片先两倍扩容,如果两倍扩容后的容量还是不够,就直接以切片需要的容量作为容量。

在go1.18之后,临界值换成了256,小于256和前面相同,大于256公式变为(oldcap+3*256)/4这个公式的值随着oldcap的越来越大,从2一直接近1.25,相对于1.18之前可以更平滑的过渡。

发现问题

看下面一行代码

运行结果如下

出现上面的结果原因是,刚开始切片容量为1,两倍扩容之后,变为2,但是2不够,所以变为所需要的容量3,后面在加4个数字,继续扩容,2倍之后是6,但是6不够应该变为需要的容量,应该为7,为什么是8呢

解决问题

先贴一下源代码,下面第一段代码是扩容机制的基础代码(1.18之前)

下面是造成上面原因的代码

下面是1.18之后的基础扩容机制

从上面代码分析之后,造成7变成8的原因就在这个roundupsize函数上面,它有一个计算公式

ini

复制代码

capmem= roundupsize(uintptr(newcap) * ptrSize)newcap= int(capmem / ptrSize)

其中ptrSize在 64 位机器下的大小为 8。 而此时newcap的值为 7,所以传入到roundupsize函数内部的值为7 * 8 = 56。接着看看roundupsize的内部:

arduino

复制代码

funcroundupsize(size uintptr)uintptr{ifsize < _MaxSmallSize {ifsize <= smallSizeMax-8{returnuintptr(class_to_size[size_to_class8[(size+smallSizeDiv-1)/smallSizeDiv]])}else{//…… } }//……}

yaml

复制代码

const_MaxSmallSize=32768constsmallSizeMax=1024constsmallSizeDiv=8varsize_to_class8=[smallSizeMax/smallSizeDiv+1]uint8{0,1,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18,18,18,19,19,19,19,20,20,20,20,21,21,21,21,22,22,22,22,23,23,23,23,24,24,24,24,25,25,25,25,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31}varclass_to_size=[_NumSizeClasses]uint16{0,8,16,,24,32,48,64,80,96,112,128,144,160,176,192,208,224,240,256,288,320,352,384,416,448,480,512,576,640,704,768,896,1024,1152,1280,1408,1536,1792,2048,2304,2688,3072,3200,3456,4096,4864,5376,6144,6528,6784,6912,8192,9472,9728,10240,10880,12288,13568,14336,16384,18432,19072,20480,21760,24576,27264,28672,32768}

roundupsize的返回值为uintptr(class_to_size[size_to_class8[(size+smallSizeDiv-1)/smallSizeDiv]]),而:

(size+smallSizeDiv-1)/smallSizeDiv = (56 + 8 - 1) / 8 = 7

size_to_class8[7] = 5

class_to_size[5] = 64所以roundopsize的返回值为 64 ,newcap = int(capmem / ptrSize) = int(64 / 8) = 8,所以最终原切片的容量扩充到了 8。

总结

如果以后遭遇情景题我们按照这个公式算的话很费时间,所以我总结了一下规则,就是如果遇见2倍扩容之后不够需要直接用它所需要的容量的情况,如果是1和3,它的容量可以是1和3,但如果是5,7,9,11等其他的奇数,全部取+1得到离他最近的偶数,因为这个值与class_to_size有关,class_to_size全部存的是8的倍数,把class_to_size里面的值全部除以8,结果为:0,1,2,3,4,6,8,10...........和咱们的规律非常吻合

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容