golang slice 源码分析

前端之家收集整理的这篇文章主要介绍了golang slice 源码分析前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

slice 结构定义

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

创建slice

// maxSliceCap returns the maximum capacity for a slice.
func maxSliceCap(elemsize uintptr) uintptr {
    if elemsize < uintptr(len(maxElems)) {
        return maxElems[elemsize]
    }
    return _MaxMem / elemsize
}

func makeslice(et *_type,len,cap int) slice {
    // NOTE: The len > maxElements check here is not strictly necessary,
    // but it produces a 'len out of range' error instead of a 'cap out of range' error
    // when someone does make([]T,bignumber). 'cap out of range' is true too,
    // but since the cap is only being supplied implicitly,saying len is clearer.
    // See issue 4085.

    // 计算最大可分配长度
    maxElements := maxSliceCap(et.size)
    if len < 0 || uintptr(len) > maxElements {
        panic(errorString("makeslice: len out of range"))
    }

    if cap < len || uintptr(cap) > maxElements {
        panic(errorString("makeslice: cap out of range"))
    }

    // 分配连续区间
    p := mallocgc(et.size*uintptr(cap),et,true)
    return slice{p,cap}
}

slice 扩容

// cap 目标容量
func growslice(et *_type,old slice,cap int) slice {
    if et.size == 0 {
        if cap < old.cap {
            panic(errorString("growslice: cap out of range"))
        }
        // 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}
    }

    newcap := old.cap
    doublecap := newcap + newcap
    if cap > doublecap {
        newcap = cap
    } else {
        // 小于1024,*2扩容
        if old.len < 1024 {
            newcap = doublecap
        } else {
            // 大于1024,*1.25
            for newcap < cap {
                newcap += newcap / 4
            }
        }
    }

    var lenmem,newlenmem,capmem uintptr
    const ptrSize = unsafe.Sizeof((*byte)(nil))
    switch et.size {
    case 1:
        lenmem = uintptr(old.len)
        newlenmem = uintptr(cap)
        capmem = roundupsize(uintptr(newcap))
        newcap = int(capmem)
    case ptrSize:
        lenmem = uintptr(old.len) * ptrSize
        newlenmem = uintptr(cap) * ptrSize
        capmem = roundupsize(uintptr(newcap) * ptrSize)
        newcap = int(capmem / ptrSize)
    default:
        lenmem = uintptr(old.len) * et.size
        newlenmem = uintptr(cap) * et.size
        capmem = roundupsize(uintptr(newcap) * et.size)
        newcap = int(capmem / et.size)
    }

    if cap < old.cap || uintptr(newcap) > maxSliceCap(et.size) {
        panic(errorString("growslice: cap out of range"))
    }

    var p unsafe.Pointer
    if et.kind&kindNoPointers != 0 {
        p = mallocgc(capmem,nil,false)
        memmove(p,old.array,lenmem)
        // 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,true)
        if !writeBarrier.enabled {
            memmove(p,lenmem)
        } else {
            for i := uintptr(0); i < lenmem; i += et.size {
                typedmemmove(et,add(p,i),add(old.array,i))
            }
        }
    }

    // 新slice
    return slice{p,newcap}
}

扩容总结: 1. 小于1024,每次扩容*2 2. 大于1024,每次扩容*1.25 3. 扩容会涉及数组拷贝,产生额外性能开销。

原文链接:https://www.f2er.com/go/187166.html

猜你在找的Go相关文章