详解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切片扩容机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅谈Go语言的代码质量保证

    浅谈Go语言的代码质量保证

    代码质量保证是一个持续的过程,需要团队的共同努力,通过结合代码风格规范、静态分析、测试、代码审查、持续集成等多种手段,可以有效提高Go语言项目的代码质量,感兴趣的可以了解一下
    2026-05-05
  • Golang time包中的函数用法详解

    Golang time包中的函数用法详解

    在日常开发过程中,会频繁遇到对时间进行操作的场景,使用 Golang 中的 time 包可以很方便地实现对时间的相关操作,接下来的几篇文章会详细讲解 time 包,本文讲解一下 time 包中的函数,需要的朋友可以参考下
    2023-08-08
  • 关于Go你不得不知道的一些实用小技巧

    关于Go你不得不知道的一些实用小技巧

    开发语言上Go成为高并发业务开发的主流语言,再加上云原生技术底座的驱动,Go语言风光无限,下面这篇文章主要给大家介绍了关于Go你不得不知道的一些实用小技巧,需要的朋友可以参考下
    2022-11-11
  • Go构建高性能的事件管理器实例详解

    Go构建高性能的事件管理器实例详解

    这篇文章主要为大家介绍了Go构建高性能的事件管理器实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • Go字符串切片操作str1[:index]的使用

    Go字符串切片操作str1[:index]的使用

    Go字符串切片str1[:index]从起始位置0到index-1截取,不复制数据,利用字符串不可变性和共享内存机制提升性能,具有一定的参考价值,感兴趣的可以了解一下
    2025-06-06
  • 从errors到pkg-errors详解Go语言中的错误处理指南

    从errors到pkg-errors详解Go语言中的错误处理指南

    错误处理是编程语言中的重要组成部分,它直接影响程序的可靠性和可维护性,本文将深入探讨Go语言中的错误处理,从标准库的errors包到第三方的pkg-errors库,帮助开发者掌握错误处理的最佳实践,提高代码的质量和可维护性
    2026-04-04
  • 使用Go重构流式日志网关的实战分享

    使用Go重构流式日志网关的实战分享

    流式日志网关的主要功能是提供 HTTP 接口,接收 CDN 边缘节点上报的各类日志(访问日志/报错日志/计费日志等),将日志作预处理并分流到多个的 Kafka 集群和 Topic 中,本文就给大家分享如何使用 Go 重构流式日志网关
    2023-06-06
  • Go 跨域中间件解决CORS问题

    Go 跨域中间件解决CORS问题

    跨域问题CORS是前端开发人员经常遇到的问题,本文主要介绍了Go 跨域中间件解决CORS问题,具有一定的参考价值,感兴趣的可以了解一下
    2025-04-04
  • golang gorm的预加载及软删硬删的数据操作示例

    golang gorm的预加载及软删硬删的数据操作示例

    这篇文章主要介绍了golang gorm的预加载及软删硬删的数据操作示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • go语言中sort包的实现方法与应用详解

    go语言中sort包的实现方法与应用详解

    golang中也实现了排序算法的包sort包,所以下面这篇文章主要给大家介绍了关于go语言中sort包的实现方法与应用的相关资料,文中通过示例代码介绍的非常详细,需要的朋友们可以参考借鉴,下面随着小编来一起学习学习吧。
    2017-11-11

最新评论