slice 扩容策略
大多数文章介绍 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
}
}
}
..........
}
例子1:一次只append一个元素
slice1 := make([]int,1,)
fmt.Println("cap of slice1",cap(slice1))
slice1 = append(slice1,1)
fmt.Println("cap of slice1",cap(slice1))
slice1 = append(slice1,2)
fmt.Println("cap of slice1",cap(slice1))
slice1024 := make([]int,1024)
fmt.Println("cap of slice1024",cap(slice1024))
slice1024 = append(slice1024,1)
fmt.Println("cap of slice1024",cap(slice1024))
slice1024 = append(slice1024,2)
fmt.Println("cap of slice1024",cap(slice1024))
/*
cap of slice1 1
cap of slice1 2
cap of slice1 4
cap of slice1024 1024
cap of slice1024 1280
cap of slice1024 1280
*/
写的测试代码 很容易验证,测试结果确实如扩展规则所说的。。。
例子2:一次只append一个元素 元素类型不相同作比较
e := []int32{1,2,3}
fmt.Println("cap of e before:",cap(e))
e = append(e,4)
fmt.Println("cap of e after:",cap(e))
f := []int64{1,2,3}
fmt.Println("cap of f before:",cap(f))
f = append(f,4)
fmt.Println("cap of f after:",cap(f))
/*
cap of e before: 3
cap of e after: 8
cap of f before: 3
cap of f after: 6
*/
如果单单是按照上边的规则 e和f的扩容策略是一抹一样的() 结果应该是一样的 都是6,但是结果并非如此。。。。
例子3:一次append多个元素 情况都一样 都是先有2个 后append3个 元素类型不相同作比较
a := []byte{1, 0}
a = append(a, 1, 1, 1)
fmt.Println("cap of a is ",cap(a))
b := []int{23, 51}
b = append(b, 4, 5, 6)
fmt.Println("cap of b is ",cap(b))
c := []int32{1, 23}
c = append(c, 2, 5, 6)
fmt.Println("cap of c is ",cap(c))
/*
cap of a is 8
cap of b is 6
cap of c is 8
*/
通过例子2 和 例子3 发现 扩容远没有上边扩容那么简单。。。
分析例子2: 扩容后按照扩容规则 cap应该选择6 6个int32 占用4X6=24个字节 但是golang内部 内存池中并没有24字节这一档,应该选择32字节的 所以32字节/ 4 = 8 cap就是8
如果是6个int64 那就不一样了 6 X 8 = 48个字节,内存池中刚好有这一档,所以 48 / 8 = 6 cap就是6
分析例子3:扩容后扩容规则 cap应该是5 5---->8 8 /1 = 8 5*8 40---->48 / 8 = 6 5*4 = 20---->32 / 4 = 8
例子4:结构体
type D struct{
age byte
bge uint32
}
d := []D{
{1,1},
{2,2},
{3,3},
}
d = append(d,D{4,4},D{5,5},D{6,6},D{7,7})
part1 := D{}
fmt.Println("cap of d is ",cap(d),unsafe.Sizeof(part1), unsafe.Alignof(part1))
/*
cap of d is 8 8 4
*/
结构体D占用8个字节 对齐是4字节对齐 原有3个 增加4个 cap初始值确实是7 不是单单的2 *3 了 所以 7 * 8 = 56 ---> 64 64/8 = 8
fmt.Printf("string size: %d\n", unsafe.Sizeof("EDDYCJY"))
fmt.Printf("string align: %d\n", unsafe.Alignof("EDDYCJY"))
type D struct{
age byte
name string
}
d := []D{
{1,"123"},
{2,"234"},
}
d = append(d,D{4,"456"},D{5,"567"},D{6,"678"})
fmt.Println("cap of d is ",cap(d),unsafe.Sizeof(d), unsafe.Alignof(d))
/*
string size: 16
string align: 8
cap of d is 5 24 8
*/
5 * 24 = 120-----> 128 128/24 = 5 所以cap = 5
符上一张 golang的 内存池 分配状况图