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}

  }