Golang slice扩容深度分析

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的 内存池 分配状况图

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值