GO中的slice使用简介(源码分析slice)
// 函数入参说明如下
//1. et 类型
//2. old 老切片
//3. cap 需要分配的指定容量,为了方便期间,调用这个函数的时候cap传递的都是老的slice的cap
func growslice(et *_type, old slice, cap int) slice {
if raceenabled { // 是否启动竞争检测
callerpc := getcallerpc()
racereadrangepc(old.array, uintptr(old.len*int(et.size)), callerpc, abi.FuncPCABIInternal(growslice))
}
if msanenabled { //内存检查,确保没有未初始化的内存被使用
msanread(old.array, uintptr(old.len*int(et.size)))
}
if asanenabled { // 检查内存访问是否越界
asanread(old.array, uintptr(old.len*int(et.size)))
}
if cap < old.cap {
panic(errorString("growslice: cap out of range"))
}
if et.size == 0 {
// 正常是不会这样的,但为了安全还是处理了0的情况
return slice{unsafe.Pointer(&zerobase), old.len, cap}
}
// 开始计算新的cap
newcap := old.cap
doublecap := newcap + newcap // 2倍
if cap > doublecap { // 新的cap要是老的2倍
newcap = cap
} else {
const threshold = 256
if old.cap < threshold { // cap小于256,newCap为oldCap的两倍
newcap = doublecap
} else {
for 0 < newcap && newcap < cap {
// Transition from growing 2x for small slices
// to growing 1.25x for large slices. This formula
// gives a smooth-ish transition between the two.
// 这个公式可以当超过256之后i,可以实现1.25到2倍的平滑过渡
newcap += (newcap + 3*threshold) / 4 // 这个公式化简一下 newCap = oldCap*1.25 + 192(3/4*256)
}
// Set newcap to the requested cap when
// the newcap calculation overflowed.
if newcap <= 0 { // 防止溢出
newcap = cap
}
}
}
// 下面的逻辑是对上面计算出来的newCap来做对齐操作,上面的计算不是真正的结果,下面还需要做内存对齐操作。
var overflow bool
var lenmem, newlenmem, capmem uintptr
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 == goarch.PtrSize:
lenmem = uintptr(old.len) * goarch.PtrSize
newlenmem = uintptr(cap) * goarch.PtrSize
capmem = roundupsize(uintptr(newcap) * goarch.PtrSize)
overflow = uintptr(newcap) > maxAlloc/goarch.PtrSize
newcap = int(capmem / goarch.PtrSize)
case isPowerOfTwo(et.size):
var shift uintptr
if goarch.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)
}
// The check of overflow in addition to capmem > maxAlloc is needed
// to prevent an overflow which can be used to trigger a segfault
// on 32bit architectures with this example program:
//
// type T [1<<27 + 1]int64
//
// var d T
// var s []T
//
// func main() {
// s = append(s, d, d, d, d)
// print(len(s), "
")
// }
if overflow || capmem > maxAlloc {
panic(errorString("growslice: cap out of range"))
}
var p unsafe.Pointer
// 下面是分配新的数组
if et.ptrdata == 0 { // 原slice底层数组为0,也就是nil切片,
p = mallocgc(capmem, nil, false)
// The append() that calls growslice is going to overwrite from old.len to cap (which will be the new length).
// Only clear the part that will not be overwritten.
memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem) // 然后使用memclrNoHeapPointers函数来清除新分配的内存
} else {
// Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory.
p = mallocgc(capmem, et, true) // 分配新的底层数组
if lenmem > 0 && writeBarrier.enabled { // 之前有数据,并且写屏障已经开启
// Only shade the pointers in old.array since we know the destination slice p
// only contains nil pointers because it has been cleared during alloc.
bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(old.array), lenmem-et.size+et.ptrdata)
}
}
// copy元素
memmove(p, old.array, lenmem)
// 创建新的切片返回
return slice{p, old.len, newcap}
}