详解golang的切片扩容机制

 更新时间:2023年07月17日 08:43:25   作者:小小的蒟蒻  
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函数上面,它有一个计算公式

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

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

func roundupsize(size uintptr) uintptr {
	if size < _MaxSmallSize {
		if size <= smallSizeMax-8 {
			return uintptr(class_to_size[size_to_class8[(size+smallSizeDiv-1)/smallSizeDiv]])
		} else {
			//……
		}
	}
    //……
}
const _MaxSmallSize = 32768
const smallSizeMax = 1024
const smallSizeDiv = 8
var size_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}
var class_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...........和咱们的规律非常吻合

到此这篇关于详解golang的切片扩容机制的文章就介绍到这了,更多相关golang切片扩容机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Golang爬虫框架 colly的使用

    Golang爬虫框架 colly的使用

    本文主要介绍了Golang爬虫框架 colly的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • 深入理解Golang的单元测试和性能测试

    深入理解Golang的单元测试和性能测试

    Go语言提供了强大的测试工具,下面这篇文章主要给大家介绍了关于Golang单元测试和性能测试的相关资料,文中通过示例代码给大家详细介绍了单元测试和性能测试的相关内容,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-08-08
  • golang如何实现mapreduce单进程版本详解

    golang如何实现mapreduce单进程版本详解

    这篇文章主要给大家介绍了关于golang如何实现mapreduce单进程版本的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-01-01
  • Go语言数据类型简单介绍

    Go语言数据类型简单介绍

    这篇文章主要介绍了Go语言数据类型简单介绍的相关资料,需要的朋友可以参考下
    2023-08-08
  • Go语言中websocket的使用demo分享

    Go语言中websocket的使用demo分享

    WebSocket是一种在单个TCP连接上进行全双工通信的协议。这篇文章主要和大家分享了一个Go语言中websocket的使用demo,需要的可以参考一下
    2022-12-12
  • golang使用json格式实现增删查改的实现示例

    golang使用json格式实现增删查改的实现示例

    这篇文章主要介绍了golang使用json格式实现增删查改的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • Go并发读写文件、分片写、分片下载文件的实现示例

    Go并发读写文件、分片写、分片下载文件的实现示例

    读写文件在很多项目中都可以用到,本文主要介绍了Go并发读写文件、分片写、分片下载文件的实现示例,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • Golang map实现原理深入分析

    Golang map实现原理深入分析

    map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用,下面这篇文章主要给大家介绍了关于golang中map使用的几点注意事项,需要的朋友可以参考下
    2023-01-01
  • Go语言中database/sql的用法介绍

    Go语言中database/sql的用法介绍

    Go语言中的database/sql包定义了对数据库的一系列操作,database/sql/driver包定义了应被数据库驱动实现的接口,这些接口会被sql包使用,本文将详细给大家介绍Go的database/sql的使用方法,需要的朋友可以参考下
    2023-05-05
  • Golang中定时器的陷阱详解

    Golang中定时器的陷阱详解

    这篇文章主要给大家介绍了关于Golang中定时器陷阱的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用golang具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-09-09

最新评论