go语言中slice的实现
slice是我们使用go语言时最经常使用的数据结构,所以我们还是有必要研究一下它的实现的,尤其是slice的扩容,具体实现参考src/runtime/slice.go。
slice定义
type slice struct {
array unsafe.Pointer
len int // 长度
cap int // 容量
}
根据growslice函数的名称,可以很容易的猜到这就是slice扩容的实现。
if et.size == 0 {
// append should not create a slice with nil pointer but non-zero len.
// We assume that append doesn't need to preserve old.array in this case.
return slice{unsafe.Pointer(&zerobase), old.len, cap} // 未创建的slice在这里创建
}
newcap := old.cap
doublecap := newcap + newcap // 新的容量是旧的容量的两倍
if cap > doublecap {
newcap = cap
} else {
if old.cap < 1024 {
newcap = doublecap // old.cap小于1024就每次乘以2扩容
} else {
// Check 0 < newcap to detect overflow
// and prevent an infinite loop.
for 0 < newcap && newcap < cap {
newcap += newcap / 4 // newcap每次增加1/4
}
// Set newcap to the requested cap when
// the newcap calculation overflowed.
if newcap <= 0 {
newcap = cap // 处理newcap溢出
}
}
}
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)
var p unsafe.Pointer
if et.ptrdata == 0 {
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)
} 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)
}
}
memmove(p, old.array, lenmem) // 拷贝旧的array

浙公网安备 33010602011771号