slice扩容
扩容的源码
func growslice(et *_type, old slice, cap int) slice {
...
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
if old.len < 1024 {
newcap = doublecap
} else {
// Check 0 < newcap to detect overflow
// and prevent an infinite loop.
for 0 < newcap && newcap < cap {
newcap += newcap / 4
}
// Set newcap to the requested cap when
// the newcap calculation overflowed.
if newcap <= 0 {
newcap = cap
}
}
}
...
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)
}
}
package main import ( "fmt" "unsafe" ) type ss struct { ptr unsafe.Pointer l int c int } type A struct { f bool b int32 a int c int64 d string } func main() { Af() return } func Af() { a := make([]A, 1, 1) b := A{} fmt.Println("Sizeof A:", unsafe.Sizeof(b)) p1 := (*ss)(unsafe.Pointer(&a)) fmt.Printf("len=%d,cap=%d,ptr=%p\n", len(a), cap(a), p1) a = append(a, A{}, A{}, A{}, A{}) fmt.Printf("len=%d,cap=%d,ptr=%p\n", len(a), cap(a), p1) for i := 0; i < 2048; i++ { a = append(a, A{}) fmt.Printf("len=%d,cap=%d,ptr=%p\n", len(a), cap(a), p1) } }
部分输出
Sizeof A: 40
len=1,cap=1,ptr=0xc00005c420
len=5,cap=5,ptr=0xc00005c420
len=6,cap=10,ptr=0xc00005c420
。。。
len=1638,cap=1638,ptr=0xc00005c420
len=1639,cap=2048,ptr=0xc00005c420
。。。
扩容分析
1、a = append(a, A{}, A{}, A{}, A{})
增加4个元素
原容量是1,元素个数增加了4个,不够存储需要扩容
newcap := old.cap //old.cap=>1
...
doublecap := newcap + newcap //2
if cap > doublecap {//cap=>5 实际的容量是5,大于2,新容量取值5
newcap = cap
}
//Sizeof A: 40
数据类型的size是40个字节
走switch的
default:
lenmem = uintptr(old.len) * et.size
newlenmem = uintptr(cap) * et.size
capmem, overflow = math.MulUintptr(et.size, uintptr(newcap)) //这里capmem=et.size*newcap即40*5=200
// 14 208 8192 39 80 8.12%
capmem = roundupsize(capmem)//内存对齐向上取整 为208
newcap = int(capmem / et.size) //208/40=5
新容量为5
len=5,cap=5,ptr=0xc00005c420
2、看一下由1638扩容到2048的情况
先看结果
len=1638,cap=1638,ptr=0xc00005c420
len=1639,cap=2048,ptr=0xc00005c42
分析:
数据类型size=40
newcap := old.cap //先取用old.cap =>1638 doublecap := newcap + newcap //1638+1638=3276 if cap > doublecap { //cap是元素实际个数为1639 newcap = cap } else { if old.len < 1024 {//old.len=1638大于1024 newcap = doublecap } else { // Check 0 < newcap to detect overflow // and prevent an infinite loop. for 0 < newcap && newcap < cap { //符合这个条件,扩容到1.25倍=>2047 newcap += newcap / 4 } // Set newcap to the requested cap when // the newcap calculation overflowed. if newcap <= 0 { newcap = cap } } } .... default: lenmem = uintptr(old.len) * et.size newlenmem = uintptr(cap) * et.size capmem, overflow = math.MulUintptr(et.size, uintptr(newcap)) //capmem=2047*40 =>81880 capmem = roundupsize(capmem) //内存对齐取整到81920 newcap = int(capmem / et.size) //81920/40=2048 //取整代码 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 { return uintptr(class_to_size[size_to_class128[(size-smallSizeMax+largeSizeDiv-1)/largeSizeDiv]]) } } if size+_PageSize < size { return size } return round(size, _PageSize) //=>81920 } func round(n, a uintptr) uintptr { return (n + a - 1) &^ (a - 1) } size是81880大于_MaxSmallSize =32768 //源码中定义的常量 _PageShift = 13 _PageSize = 1 << _PageShift //1<<13 =>8192
参考链接