本例子基于go1.17.x版本
package main
import "fmt"
func main(){
s := []int{1,2}
oldCap := cap(s)
s = append(s, 3,4,5)
newCap := cap(s)
fmt.Printf("oldCap = %d, newCap = %d",oldCap,newCap)
}
根据例子,我们看到s一开始cap是2,当添加元素3的时候,容量变成4;添加元素4的时候,容量不变;添加5的时候,容量cap是不是应该是8呢?这里很多朋友以为应该都是8呀,不是说slice容量小于1024的时候,新slice的容量就变成原来的2倍吗?为何这里是=6?
我们打开slice的源码看看:
源码目录src/runtime/slice.go
我们找到这个方法:
func growslice(et *_type, old slice, cap int) slice d
当我们添加元素5的时候,一个临时的cap=5,作为第三个参数传入growslice的里面
newcap := old.cap doublecap := newcap + newcap if cap > doublecap { newcap = cap }
newcap = oldcap = 2
doublecap = 4
cap =5 > doublecap
=》newcap = 5
接着源码我们看到,进入了
capmem = roundupsize(uintptr(newcap) * sys.PtrSize)
4 << (^uintptr(0) >> 63) sys.PrtSize 32系统是4,64位系统就是8
我电脑是64位的,所以这里capmen = 5 * 8 = 40
继续传入roudupsize方法中:
func roundupsize(size uintptr) uintptr { if size < _MaxSmallSize { if size <= smallSizeMax-8 { return uintptr(class_to_size[size_to_class8[divRoundUp(size, smallSizeDiv)]]) } else { return uintptr(class_to_size[size_to_class128[divRoundUp(size-smallSizeMax, largeSizeDiv)]]) } } if size+_PageSize < size { return size } return alignUp(size, _PageSize) }
通过源码,我们看到:
const ( _MaxSmallSize = 32768 smallSizeDiv = 8 smallSizeMax = 1024 largeSizeDiv = 128 _NumSizeClasses = 68 _PageShift = 13 )
这里我们的40 < _MaxSmallSize,且<smallSizeMax 所以得到
return uintptr(class_to_size[size_to_class8[divRoundUp(size, smallSizeDiv)]])
func divRoundUp(n, a uintptr) uintptr { // a is generally a power of two. This will get inlined and // the compiler will optimize the division. return (n + a - 1) / a }
divRoundUp就是一个取余的运算,(40 + 8 - 1)/8 = 5
size_to_class8是啥?我们继续看源码,看到它是系统定义的一个[smallSizeMax/smallSizeDiv + 1]uint8数组
var size_to_class8 = [smallSizeMax/smallSizeDiv + 1]uint8{0, 1, 2, 3, 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, 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, 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, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}
我们拿取余的5作为索引,在数组中取值就是5,
继续在class_to_size中取值:
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}
继续用5当做索引,取值就是48
最后计算
newcap = int(capmem / sys.PtrSize)
sys.PtrSize = 8,这里就是48/8 = 6
今天就分享到这里
有疑问加站长微信联系(非本文作者)