欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

golang中slice擴(kuò)容的具體實(shí)現(xiàn)

 更新時(shí)間:2025年05月23日 10:42:30   作者:xsh219  
Go 語言中的切片擴(kuò)容機(jī)制是 Go 運(yùn)行時(shí)的一個(gè)關(guān)鍵部分,它確保切片在動(dòng)態(tài)增加元素時(shí)能夠高效地管理內(nèi)存,本文主要介紹了golang中slice擴(kuò)容的具體實(shí)現(xiàn),感興趣的可以了解一下

Go 語言中的切片擴(kuò)容機(jī)制是 Go 運(yùn)行時(shí)的一個(gè)關(guān)鍵部分,它確保切片在動(dòng)態(tài)增加元素時(shí)能夠高效地管理內(nèi)存。這個(gè)機(jī)制是在 Go 運(yùn)行時(shí)內(nèi)部實(shí)現(xiàn)的,涉及了內(nèi)存分配、數(shù)據(jù)拷貝和容量調(diào)整。擴(kuò)容的實(shí)現(xiàn)主要體現(xiàn)在 runtime.growslice 函數(shù)中。下面我們將深入分析 Go 切片的擴(kuò)容機(jī)制,結(jié)合源碼來進(jìn)行詳細(xì)解答。

1. 切片擴(kuò)容的觸發(fā)

切片是 Go 中的一種動(dòng)態(tài)數(shù)組,它有 長(zhǎng)度(len) 和 容量(cap)。當(dāng)我們向切片添加元素時(shí),切片的長(zhǎng)度會(huì)增加。如果長(zhǎng)度超出了當(dāng)前的容量,Go 會(huì)自動(dòng)擴(kuò)容。切片擴(kuò)容的過程是通過 append 函數(shù)來觸發(fā)的。

append 函數(shù)的實(shí)現(xiàn)

append 函數(shù)是 Go 內(nèi)置的函數(shù),用于向切片追加元素。它的實(shí)現(xiàn)會(huì)首先檢查當(dāng)前切片的容量是否足夠容納新的元素,如果不夠,它會(huì)調(diào)用 runtime.growslice 函數(shù)來擴(kuò)容。

// append() 源碼簡(jiǎn)化版
func append(slice []T, elems ...T) []T {
    // 1. 檢查當(dāng)前切片是否有足夠的容量
    if len(slice)+len(elems) <= cap(slice) {
        // 如果容量足夠,直接追加元素
        slice = slice[:len(slice)+len(elems)]
        copy(slice[len(slice)-len(elems):], elems)
        return slice
    }
    // 2. 如果容量不足,進(jìn)行擴(kuò)容
    return growslice(reflect.TypeOf(slice).Elem(), slice, len(slice)+len(elems))
}

2. runtime.growslice 函數(shù)

當(dāng)切片容量不足時(shí),append 會(huì)調(diào)用 growslice 函數(shù)來擴(kuò)容。growslice 函數(shù)是 Go 運(yùn)行時(shí)內(nèi)部函數(shù),負(fù)責(zé)實(shí)際的內(nèi)存分配和切片擴(kuò)容操作。

growslice 函數(shù)源碼

// growslice 擴(kuò)容的實(shí)現(xiàn)
func growslice(et *_type, old slice, cap int) slice {
    if cap < old.cap {
        panic("growslice: cap out of range")
    }

    // 如果元素大小為 0,直接返回新的切片
    if et.size == 0 {
        return slice{unsafe.Pointer(&zerobase), old.len, cap}
    }

    // 擴(kuò)容策略:根據(jù)當(dāng)前容量和預(yù)期的新容量來決定新容量
    newcap := old.cap
    doublecap := newcap + newcap
    if cap > doublecap {
        newcap = cap
    } else {
        const threshold = 256
        if old.cap < threshold {
            newcap = doublecap
        } else {
            for 0 < newcap && newcap < cap {
                newcap += (newcap + 3*threshold) / 4
            }
            if newcap <= 0 {
                newcap = cap
            }
        }
    }

    // 根據(jù)擴(kuò)容后的容量計(jì)算所需的內(nèi)存空間
    var lenmem, newlenmem, capmem uintptr
    switch {
    case et.size == 1:
        lenmem = uintptr(old.len)
        newlenmem = uintptr(cap)
        capmem = roundupsize(uintptr(newcap))
        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)
        newcap = int(capmem / goarch.PtrSize)
    case isPowerOfTwo(et.size):
        var shift uintptr
        if goarch.PtrSize == 8 {
            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)
        newcap = int(capmem >> shift)
    default:
        lenmem = uintptr(old.len) * et.size
        newlenmem = uintptr(cap) * et.size
        capmem, _ = math.MulUintptr(et.size, uintptr(newcap))
        capmem = roundupsize(capmem)
        newcap = int(capmem / et.size)
    }

    // 分配新的內(nèi)存,并拷貝舊的數(shù)據(jù)
    var p unsafe.Pointer
    if et.ptrdata == 0 {
        p = mallocgc(capmem, nil, false)
    } else {
        p = mallocgc(capmem, et, true)
    }

    memmove(p, old.array, lenmem)
    return slice{p, old.len, newcap}
}

3. 擴(kuò)容的具體過程

  • 檢查容量是否足夠: growslice 首先會(huì)檢查請(qǐng)求的擴(kuò)容是否超過了當(dāng)前切片的容量。如果新容量大于原容量的兩倍,它會(huì)直接使用新容量作為目標(biāo)容量。

  • 根據(jù)擴(kuò)容策略計(jì)算新容量:

    • 如果切片的容量小于 256,它會(huì)直接翻倍容量(newcap = cap)。

    •  如果切片容量較大,擴(kuò)容時(shí)會(huì)采取逐步增加的策略:按原容量的 1.25 倍+192 (也就是3*threshold / 4) 增長(zhǎng),直到滿足目標(biāo)容量。

  • 計(jì)算內(nèi)存所需大?。?nbsp;growslice 會(huì)計(jì)算新切片所需的內(nèi)存大小,并分配新的內(nèi)存空間。對(duì)于不同類型的元素(如基本類型、指針類型等),內(nèi)存分配的方式會(huì)有所不同。

  • 內(nèi)存分配和數(shù)據(jù)拷貝:

    • 通過 mallocgc 分配新的內(nèi)存區(qū)域,在分配空間的時(shí)候因根據(jù)標(biāo)簽的大小等級(jí)會(huì)向上取整,go在擴(kuò)容的時(shí)候?qū)幵笭奚徊糠謨?nèi)部碎片也要保證外部盡可能少的外部碎片,并使用 memmove 將原切片的數(shù)據(jù)拷貝到新的內(nèi)存中。

    • 擴(kuò)容后的切片會(huì)更新 指針(指向新的內(nèi)存)、長(zhǎng)度 和 容量,然后返回新的切片。

4. 擴(kuò)容的性能考慮

切片擴(kuò)容的過程需要進(jìn)行 內(nèi)存分配 和 數(shù)據(jù)拷貝,這會(huì)影響性能,尤其是在頻繁擴(kuò)容的場(chǎng)景中。

  • 頻繁擴(kuò)容的開銷:每次擴(kuò)容時(shí),都會(huì)重新分配內(nèi)存并將數(shù)據(jù)拷貝到新的內(nèi)存區(qū)域,這會(huì)帶來額外的時(shí)間和空間開銷。

  • 容量預(yù)估:為了避免頻繁擴(kuò)容,最好在創(chuàng)建切片時(shí)預(yù)估切片需要的容量,尤其是在元素?cái)?shù)量已知的情況下。可以使用 make([]T, 0, n) 來預(yù)分配一個(gè)足夠的容量,以減少擴(kuò)容操作。

5. 擴(kuò)容策略的總結(jié)

  • 小容量時(shí)(比如容量小于 256),切片會(huì)按 2 倍擴(kuò)容。

  • 大容量時(shí),Go 會(huì)使用 1.25 倍擴(kuò)容 + 192 的策略,確保不會(huì)過度浪費(fèi)內(nèi)存。

  • 對(duì)于 nil 或零容量切片,growslice 會(huì)初始化并分配合適的容量。

6. 示例:切片擴(kuò)容過程

下面是一個(gè)示例,展示了切片擴(kuò)容的過程:

package main

import "fmt"

func main() {
    var slice []int
    for i := 0; i < 10; i++ {
        slice = append(slice, i)
        fmt.Printf("Length: %d, Capacity: %d, Slice: %v\n", len(slice), cap(slice), slice)
    }
}

輸出示例:

Length: 1, Capacity: 1, Slice: [0]
Length: 2, Capacity: 2, Slice: [0 1]
Length: 3, Capacity: 4, Slice: [0 1 2]
Length: 4, Capacity: 4, Slice: [0 1 2 3]
Length: 5, Capacity: 8, Slice: [0 1 2 3 4]
Length: 6, Capacity: 8, Slice: [0 1 2 3 4 5]
Length: 7, Capacity: 8, Slice: [0 1 2 3 4 5 6]
Length: 8, Capacity: 8, Slice: [0 1 2 3 4 5 6 7]
Length: 9, Capacity: 16, Slice: [0 1 2 3 4 5 6 7 8]
Length: 10, Capacity: 16, Slice: [0 1 2 3 4 5 6 7 8 9]

如上所示,切片的容量在每次添加元素時(shí)會(huì)根據(jù)擴(kuò)容策略進(jìn)行增長(zhǎng)。初始容量為 1,當(dāng)容量不足時(shí),擴(kuò)容后容量翻倍。最終,容量達(dá)到 16。

總結(jié)

Go 的切片擴(kuò)容機(jī)制是通過 append 函數(shù)和 growslice 函數(shù)實(shí)現(xiàn)的。在擴(kuò)容時(shí),Go 會(huì)根據(jù)當(dāng)前切片的容量和預(yù)期的容量進(jìn)行計(jì)算,并分配新的內(nèi)存空間,拷貝原有數(shù)據(jù)。擴(kuò)容的策略通常是小容量時(shí)翻倍,大容量時(shí)逐步增加,保證了內(nèi)存的高效使用。

到此這篇關(guān)于golang中slice擴(kuò)容的具體實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)golang slice擴(kuò)容內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go 簡(jiǎn)單實(shí)現(xiàn)多租戶數(shù)據(jù)庫(kù)隔離

    Go 簡(jiǎn)單實(shí)現(xiàn)多租戶數(shù)據(jù)庫(kù)隔離

    本文主要介紹了Go 簡(jiǎn)單實(shí)現(xiàn)多租戶數(shù)據(jù)庫(kù)隔離,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • Makefile構(gòu)建Golang項(xiàng)目示例詳解

    Makefile構(gòu)建Golang項(xiàng)目示例詳解

    這篇文章主要為大家介紹了Makefile構(gòu)建Golang項(xiàng)目的過程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • gorm golang 并發(fā)連接數(shù)據(jù)庫(kù)報(bào)錯(cuò)的解決方法

    gorm golang 并發(fā)連接數(shù)據(jù)庫(kù)報(bào)錯(cuò)的解決方法

    今天小編就為大家分享一篇gorm golang 并發(fā)連接數(shù)據(jù)庫(kù)報(bào)錯(cuò)的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2019-07-07
  • go內(nèi)存隊(duì)列l(wèi)ist VS slice實(shí)現(xiàn)方式對(duì)比分析

    go內(nèi)存隊(duì)列l(wèi)ist VS slice實(shí)現(xiàn)方式對(duì)比分析

    這篇文章主要為大家介紹了go內(nèi)存隊(duì)列l(wèi)ist VS slice實(shí)現(xiàn)方式對(duì)比分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • Go語言的匿名字段實(shí)現(xiàn)組合復(fù)用實(shí)例探究

    Go語言的匿名字段實(shí)現(xiàn)組合復(fù)用實(shí)例探究

    這篇文章主要為大家介紹了Go語言的匿名字段實(shí)現(xiàn)組合復(fù)用實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • golang調(diào)用shell命令(實(shí)時(shí)輸出,終止)

    golang調(diào)用shell命令(實(shí)時(shí)輸出,終止)

    本文主要介紹了golang調(diào)用shell命令(實(shí)時(shí)輸出,終止),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • Go語言學(xué)習(xí)教程之goroutine和通道的示例詳解

    Go語言學(xué)習(xí)教程之goroutine和通道的示例詳解

    這篇文章主要通過A?Tour?of?Go中的例子進(jìn)行學(xué)習(xí),以此了解Go語言中的goroutine和通道,文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-09-09
  • go語言實(shí)現(xiàn)依賴注入的示例代碼

    go語言實(shí)現(xiàn)依賴注入的示例代碼

    依賴注入和控制反轉(zhuǎn)恰恰相反,它是一種具體的編碼技巧,我們不通過 new 的方式在類內(nèi)部創(chuàng)建依賴類的對(duì)象,而是將依賴的類對(duì)象在外部創(chuàng)建好之后,通過構(gòu)造函數(shù)、函數(shù)參數(shù)等方式傳遞給類來使用,本文將給大家介紹go語言實(shí)現(xiàn)依賴注入,需要的朋友可以參考下
    2024-01-01
  • Go?的入口函數(shù)和包初始化的使用

    Go?的入口函數(shù)和包初始化的使用

    本文主要介紹了Go?的入口函數(shù)和包初始化的使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • Go?panic的三種產(chǎn)生方式細(xì)節(jié)探究

    Go?panic的三種產(chǎn)生方式細(xì)節(jié)探究

    這篇文章主要介紹了Go?panic的三種產(chǎn)生方式細(xì)節(jié)探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12

最新評(píng)論