先来看一段代码
- code:
- e := []int64{1, 2, 3}
- fmt.Println("cap of e before:", cap(e))
- e = append(e, 4, 5, 6, 7)
- fmt.Println("cap of e after:", cap(e))
-
- output:
-
- cap of e before: 3
- cap of e after: 8
为什么容量是8?
append了的4个元素,如果是原来的2倍也才6个,小于长度7,所以容量赋值长度7
内存分配,为了高效使用需要对齐,找到合适的span对象
capmem = roundupsize(uintptr(newcap) * ptrSize) ptrSize 8字节
capmem = roundupsize(7*8)
capmem = 64
newcap = int(capmem / ptrSize)
newcap = 8
- _MaxSmallSize = 32768
- smallSizeDiv = 8
- smallSizeMax = 1024
- largeSizeDiv = 128
- _NumSizeClasses = 68
- _PageShift = 13
- maxObjsPerSpan = 1024
- )
-
- 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}
- var class_to_allocnpages = [_NumSizeClasses]uint8{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 3, 2, 3, 1, 3, 2, 3, 4, 5, 6, 1, 7, 6, 5, 4, 3, 5, 7, 2, 9, 7, 5, 8, 3, 10, 7, 4}
- var class_to_divmagic = [_NumSizeClasses]uint32{0, ^uint32(0)/8 + 1, ^uint32(0)/16 + 1, ^uint32(0)/24 + 1, ^uint32(0)/32 + 1, ^uint32(0)/48 + 1, ^uint32(0)/64 + 1, ^uint32(0)/80 + 1, ^uint32(0)/96 + 1, ^uint32(0)/112 + 1, ^uint32(0)/128 + 1, ^uint32(0)/144 + 1, ^uint32(0)/160 + 1, ^uint32(0)/176 + 1, ^uint32(0)/192 + 1, ^uint32(0)/208 + 1, ^uint32(0)/224 + 1, ^uint32(0)/240 + 1, ^uint32(0)/256 + 1, ^uint32(0)/288 + 1, ^uint32(0)/320 + 1, ^uint32(0)/352 + 1, ^uint32(0)/384 + 1, ^uint32(0)/416 + 1, ^uint32(0)/448 + 1, ^uint32(0)/480 + 1, ^uint32(0)/512 + 1, ^uint32(0)/576 + 1, ^uint32(0)/640 + 1, ^uint32(0)/704 + 1, ^uint32(0)/768 + 1, ^uint32(0)/896 + 1, ^uint32(0)/1024 + 1, ^uint32(0)/1152 + 1, ^uint32(0)/1280 + 1, ^uint32(0)/1408 + 1, ^uint32(0)/1536 + 1, ^uint32(0)/1792 + 1, ^uint32(0)/2048 + 1, ^uint32(0)/2304 + 1, ^uint32(0)/2688 + 1, ^uint32(0)/3072 + 1, ^uint32(0)/3200 + 1, ^uint32(0)/3456 + 1, ^uint32(0)/4096 + 1, ^uint32(0)/4864 + 1, ^uint32(0)/5376 + 1, ^uint32(0)/6144 + 1, ^uint32(0)/6528 + 1, ^uint32(0)/6784 + 1, ^uint32(0)/6912 + 1, ^uint32(0)/8192 + 1, ^uint32(0)/9472 + 1, ^uint32(0)/9728 + 1, ^uint32(0)/10240 + 1, ^uint32(0)/10880 + 1, ^uint32(0)/12288 + 1, ^uint32(0)/13568 + 1, ^uint32(0)/14336 + 1, ^uint32(0)/16384 + 1, ^uint32(0)/18432 + 1, ^uint32(0)/19072 + 1, ^uint32(0)/20480 + 1, ^uint32(0)/21760 + 1, ^uint32(0)/24576 + 1, ^uint32(0)/27264 + 1, ^uint32(0)/28672 + 1, ^uint32(0)/32768 + 1}
- 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}
- var size_to_class128 = [(_MaxSmallSize-smallSizeMax)/largeSizeDiv + 1]uint8{32, 33, 34, 35, 36, 37, 37, 38, 38, 39, 39, 40, 40, 40, 41, 41, 41, 42, 43, 43, 44, 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, 49, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67}
-
- 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
- }
-
- 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)
- }
计算容量
< 256 容量*2,反之
容量 += 容量+3*256 >> 2 (二进制中右移n位,相当于除以2的n次方_
举例512扩容
512 += (512 + 256 * 3)>>2
cap = 512 + 320 = 832
- func nextslicecap(newLen, oldCap int) int {
- newcap := oldCap
- doublecap := newcap + newcap
- if newLen > doublecap {
- return newLen
- }
-
- const threshold = 256
- if oldCap < threshold {
- return doublecap
- }
- for {
- // Transition from growing 2x for small slices
- // to growing 1.25x for large slices. This formula
- // gives a smooth-ish transition between the two.
- newcap += (newcap + 3*threshold) >> 2
-
- // We need to check `newcap >= newLen` and whether `newcap` overflowed.
- // newLen is guaranteed to be larger than zero, hence
- // when newcap overflows then `uint(newcap) > uint(newLen)`.
- // This allows to check for both with the same comparison.
- if uint(newcap) >= uint(newLen) {
- break
- }
- }
-
- // Set newcap to the requested cap when
- // the newcap calculation overflowed.
- if newcap <= 0 {
- return newLen
- }
- return newcap
- }
-
-
- func growslice(oldPtr unsafe.Pointer, newLen, oldCap, num int, et *_type) slice {
- ...
-
- newcap := nextslicecap(newLen, oldCap)
-
- var overflow bool
- var lenmem, newlenmem, capmem uintptr
- // Specialize for common values of et.Size.
- // For 1 we don't need any division/multiplication.
- // For goarch.PtrSize, compiler will optimize division/multiplication into a shift by a constant.
- // For powers of 2, use a variable shift.
- switch {
- case et.Size_ == 1:
- lenmem = uintptr(oldLen)
- newlenmem = uintptr(newLen)
- capmem = roundupsize(uintptr(newcap))
- overflow = uintptr(newcap) > maxAlloc
- newcap = int(capmem)
- case et.Size_ == goarch.PtrSize:
- lenmem = uintptr(oldLen) * goarch.PtrSize
- newlenmem = uintptr(newLen) * goarch.PtrSize
- capmem = roundupsize(uintptr(newcap) * goarch.PtrSize)
- overflow = uintptr(newcap) > maxAlloc/goarch.PtrSize
- newcap = int(capmem / goarch.PtrSize)
- case isPowerOfTwo(et.Size_):
- var shift uintptr
- if goarch.PtrSize == 8 {
- // Mask shift for better code generation.
- shift = uintptr(sys.TrailingZeros64(uint64(et.Size_))) & 63
- } else {
- shift = uintptr(sys.TrailingZeros32(uint32(et.Size_))) & 31
- }
- lenmem = uintptr(oldLen) << shift
- newlenmem = uintptr(newLen) << shift
- capmem = roundupsize(uintptr(newcap) << shift)
- overflow = uintptr(newcap) > (maxAlloc >> shift)
- newcap = int(capmem >> shift)
- capmem = uintptr(newcap) << shift
- default:
- lenmem = uintptr(oldLen) * et.Size_
- newlenmem = uintptr(newLen) * et.Size_
- capmem, overflow = math.MulUintptr(et.Size_, uintptr(newcap))
- capmem = roundupsize(capmem)
- newcap = int(capmem / et.Size_)
- capmem = uintptr(newcap) * et.Size_
- }
-
- ...
- memmove(p, oldPtr, lenmem)
-
- return slice{p, newLen, newcap}
- }