funcgrowslice(et*_type,oldslice,capint)slice{// 静态分析, 内存扫描
// ...
ifcap<old.cap{panic(errorString("growslice: cap out of range"))}// 如果存储的类型空间为0, 比如说 []struct{}, 数据为空,长度不为空
ifet.size==0{returnslice{unsafe.Pointer(&zerobase),old.len,cap}}newcap:=old.capdoublecap:=newcap+newcapifcap>doublecap{// 如果新容量大于原有容量的两倍,则直接按照新增容量大小申请
newcap=cap}else{ifold.len<1024{// 如果原有长度小于1024,那新容量是老容量的2倍
newcap=doublecap}else{// 按照原有容量的1/4 增加,直到满足新容量的需要
for0<newcap&&newcap<cap{newcap+=newcap/4}// 通过校验newcap 大于0检查容量是否溢出。
ifnewcap<=0{newcap=cap}}}varoverflowboolvarlenmem,newlenmem,capmemuintptr// 为了加速计算(少用除法,乘法)
// 对于不同的slice元素大小,选择不同的计算方法
// 获取需要申请的内存大小。
switch{caseet.size==1:lenmem=uintptr(old.len)newlenmem=uintptr(cap)capmem=roundupsize(uintptr(newcap))overflow=uintptr(newcap)>maxAllocnewcap=int(capmem)caseet.size==sys.PtrSize:lenmem=uintptr(old.len)*sys.PtrSizenewlenmem=uintptr(cap)*sys.PtrSizecapmem=roundupsize(uintptr(newcap)*sys.PtrSize)overflow=uintptr(newcap)>maxAlloc/sys.PtrSizenewcap=int(capmem/sys.PtrSize)caseisPowerOfTwo(et.size):// 二的倍数,用位移运算
varshiftuintptrifsys.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)<<shiftnewlenmem=uintptr(cap)<<shiftcapmem=roundupsize(uintptr(newcap)<<shift)overflow=uintptr(newcap)>(maxAlloc>>shift)newcap=int(capmem>>shift)default:// 其他用除法
lenmem=uintptr(old.len)*et.sizenewlenmem=uintptr(cap)*et.sizecapmem,overflow=math.MulUintptr(et.size,uintptr(newcap))capmem=roundupsize(capmem)newcap=int(capmem/et.size)}// 判断是否会溢出
ifoverflow||capmem>maxAlloc{panic(errorString("growslice: cap out of range"))}// 内存分配
varpunsafe.Pointerifet.kind&kindNoPointers!=0{p=mallocgc(capmem,nil,false)// 清空不需要数据拷贝的部分内存
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)ifwriteBarrier.enabled{// gc 相关
// 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)}}// 数据拷贝
memmove(p,old.array,lenmem)returnslice{p,old.len,newcap}}
funcslicecopy(to,fmslice,widthuintptr)int{iffm.len==0||to.len==0{return0}n:=fm.lenifto.len<n{n=to.len}// 元素大小为0,则直接返回
ifwidth==0{returnn}// 竟态分析和内存扫描
// ...
size:=uintptr(n)*width// 直接内存拷贝
ifsize==1{// common case worth about 2x to do here
*(*byte)(to.array)=*(*byte)(fm.array)// known to be a byte pointer
}else{memmove(to.array,fm.array,size)}returnn}// 字符串slice的拷贝
funcslicestringcopy(to[]byte,fmstring)int{iflen(fm)==0||len(to)==0{return0}n:=len(fm)iflen(to)<n{n=len(to)}// 竟态分析和内存扫描
// ...
memmove(unsafe.Pointer(&to[0]),stringStructOf(&fm).str,uintptr(n))returnn}
其他
汇编的生成方法
1
go tool compile -N -S slice.go > slice.S
需要了解unsafe.Pointer 的使用
slice.go 位于 runtime/slice.go
上述代码使用 go1.12.5 版本
还有一点需要提醒, type 长度为0的对象。比如说 struct{} 类型。(所以,很多使用chan struct{} 做channel 的传递,节省内存)