// runtime/slice.go // et:表示slice的一个元素;old:表示旧的slice; cap:表示新切片需要的容量; funcgrowslice(et *_type, old slice, capint) slice { ifcap < old.cap { panic(errorString("growslice: cap out of range")) }
if et.size == 0 { // append should not create a slice with nil pointer but non-zero len. // We assume that append doesn't need to preserve old.array in this case. return slice{unsafe.Pointer(&zerobase), old.len, cap} }
newcap := old.cap // 两倍扩容 doublecap := newcap + newcap // 新切片需要的容量大于当前容量的两倍,则直接按照新切片需要的容量扩容 ifcap > doublecap { newcap = cap } else { // 原 slice 容量小于 1024 的时候,新 slice 容量按2倍扩容 if old.cap < 1024 { newcap = doublecap } else { // 原 slice 容量超过 1024,新 slice 容量变成原来的1.25倍。 // Check 0 < newcap to detect overflow // and prevent an infinite loop. for0 < newcap && newcap < cap { newcap += newcap / 4 } // Set newcap to the requested cap when // the newcap calculation overflowed. if newcap <= 0 { newcap = cap } } }
// 后半部分还对 newcap 作了一个内存对齐,这个和内存分配策略相关。进行内存对齐之后,新 slice 的容量是要 大于等于 老 slice 容量的 2倍或者1.25倍。 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 sys.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(old.len) newlenmem = uintptr(cap) capmem = roundupsize(uintptr(newcap)) overflow = uintptr(newcap) > maxAlloc newcap = int(capmem) case et.size == sys.PtrSize: lenmem = uintptr(old.len) * sys.PtrSize newlenmem = uintptr(cap) * sys.PtrSize capmem = roundupsize(uintptr(newcap) * sys.PtrSize) overflow = uintptr(newcap) > maxAlloc/sys.PtrSize newcap = int(capmem / sys.PtrSize) case isPowerOfTwo(et.size): var shift uintptr if sys.PtrSize == 8 { // Mask shift for better code generation. shift = uintptr(sys.Ctz64(uint64(et.size))) & 63 } else { shift = uintptr(sys.Ctz32(uint32(et.size))) & 31 } lenmem = uintptr(old.len) << shift newlenmem = uintptr(cap) << shift capmem = roundupsize(uintptr(newcap) << shift) overflow = uintptr(newcap) > (maxAlloc >> shift) newcap = int(capmem >> shift) default: lenmem = uintptr(old.len) * et.size newlenmem = uintptr(cap) * et.size capmem, overflow = math.MulUintptr(et.size, uintptr(newcap)) capmem = roundupsize(capmem) newcap = int(capmem / et.size) } }
打印扩容的容量
1 2 3 4 5 6 7 8 9 10 11
package main
import ( "fmt" )
funcmain() { for i := 0; i < 2000; i += 100 { fmt.Println(i, cap(append(make([]bool, i), true))) } }