我注意到,当容量为奇数时,切片的容量表现为不同的方式。更具体地说:将元素添加到切片时,如果原始容量为偶数,则切片的容量将 增加一倍 。但是,当原始容量为奇数时,容量将 增加1,然后再增加一倍 。例:
s := make([]int, 28, 28) s = append(s, 1) fmt.Println("len=", len(s), " cap=", cap(s)) // len = len + 1, cap = 2 * cap pri := make([]int, 27, 27) pri = append(pri, 1) fmt.Println("len=", len(pri), " cap=", cap(pri)) // len = len + 1, cap = 2 * (cap + 1)
假设这不是错误,那么此行为的原因是什么?
链接到游乐场:http : //play.golang.org/p/wfmdobgCUF
它正在舍入切片容量以填充分配的内存块。
让我们看一下Go1.5.1源代码:
https://github.com/golang/go/blob/f2e4c8b5fb3660d793b2c545ef207153db0a34b1/src/cmd/compile/internal/gc/walk.go#L2895告诉我们,append(l1, l2...)扩展为
append(l1, l2...)
s := l1 if n := len(l1) + len(l2) - cap(s); n > 0 { s = growslice_n(s, n) } s = s[:len(l1)+len(l2)] memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
我们感兴趣的部分在growslice_n此处定义:https : //github.com/golang/go/blob/f2e4c8b5fb3660d793b2c545ef207153db0a34b1/src/runtime/slice.go#L36
growslice_n
再深入一点,我们发现:
newcap := old.cap if newcap+newcap < cap { newcap = cap } else { for { if old.len < 1024 { newcap += newcap } else { newcap += newcap / 4 } if newcap >= cap { break } } } /* [...] */ capmem := roundupsize(uintptr(newcap) * uintptr(et.size)) newcap = int(capmem / uintptr(et.size))
roundupsize在此处定义:https : //github.com/golang/go/blob/f2e4c8b5fb3660d793b2c545ef207153db0a34b1/src/runtime/msize.go#L178
roundupsize
// Returns size of the memory block that mallocgc will allocate if you ask for the size. func roundupsize(size uintptr) uintptr { if size < _MaxSmallSize { if size <= 1024-8 { return uintptr(class_to_size[size_to_class8[(size+7)>>3]]) } else { return uintptr(class_to_size[size_to_class128[(size-1024+127)>>7]]) } } if size+_PageSize < size { return size } return round(size, _PageSize) }
它是在那里介绍的:https : //groups.google.com/forum/#!topic/golang- codereviews/bFGtI4Cpb_M
当切片增加时,请考虑分配的内存块的大小。