最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

golang中slice擴容的具體實現(xiàn)

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

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

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

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

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

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

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

2. runtime.growslice 函數(shù)

當切片容量不足時,append 會調(diào)用 growslice 函數(shù)來擴容。growslice 函數(shù)是 Go 運行時內(nèi)部函數(shù),負責實際的內(nèi)存分配和切片擴容操作。

growslice 函數(shù)源碼

// growslice 擴容的實現(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}
    }

    // 擴容策略:根據(jù)當前容量和預期的新容量來決定新容量
    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ù)擴容后的容量計算所需的內(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. 擴容的具體過程

  • 檢查容量是否足夠: growslice 首先會檢查請求的擴容是否超過了當前切片的容量。如果新容量大于原容量的兩倍,它會直接使用新容量作為目標容量。

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

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

    •  如果切片容量較大,擴容時會采取逐步增加的策略:按原容量的 1.25 倍+192 (也就是3*threshold / 4) 增長,直到滿足目標容量。

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

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

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

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

4. 擴容的性能考慮

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

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

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

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

  • 小容量時(比如容量小于 256),切片會按 2 倍擴容。

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

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

6. 示例:切片擴容過程

下面是一個示例,展示了切片擴容的過程:

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]

如上所示,切片的容量在每次添加元素時會根據(jù)擴容策略進行增長。初始容量為 1,當容量不足時,擴容后容量翻倍。最終,容量達到 16。

總結(jié)

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

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

相關(guān)文章

  • Go 語言結(jié)構(gòu)實例分析

    Go 語言結(jié)構(gòu)實例分析

    在本篇文章里小編給大家整理的是一篇關(guān)于Go 語言結(jié)構(gòu)實例分析的相關(guān)知識點,有興趣的朋友們可以學習下。
    2021-07-07
  • go?語言爬蟲庫goquery的具體使用

    go?語言爬蟲庫goquery的具體使用

    GoQuery是專為Go語言設(shè)計的一個強大的HTML解析和查詢庫,本文主要介紹了go語言爬蟲庫goquery的具體使用,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • 詳解Go語言中的Slice鏈式操作

    詳解Go語言中的Slice鏈式操作

    這篇文章主要為大家詳細介紹了Go語言中的Slice鏈式操作的相關(guān)知識,文中的示例代碼講解詳細,具有一定的學習價值,感興趣的小伙伴可以了解一下
    2023-04-04
  • golang字符編碼的實現(xiàn)

    golang字符編碼的實現(xiàn)

    本文主要介紹了golang字符編碼的實現(xiàn),,有三種編碼形式:UTF-8,UTF-16,UTF-32,下面就來介紹一下如何使用,感興趣的可以了解一下
    2024-08-08
  • Go語言實現(xiàn)聊天小工具的示例代碼

    Go語言實現(xiàn)聊天小工具的示例代碼

    這篇文章主要為大家詳細介紹了如何利用Go語言實現(xiàn)聊天小工具,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-03-03
  • Windows下Goland的環(huán)境搭建過程詳解

    Windows下Goland的環(huán)境搭建過程詳解

    這篇文章主要介紹了Windows下Goland的環(huán)境搭建過程,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10
  • 詳解golang 模板(template)的常用基本語法

    詳解golang 模板(template)的常用基本語法

    這篇文章主要介紹了詳解golang 模板(template)的常用基本語法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-08-08
  • Golang中g(shù)RPC內(nèi)置Trace的實現(xiàn)

    Golang中g(shù)RPC內(nèi)置Trace的實現(xiàn)

    gRPC內(nèi)置了基于golang.org/x/net/trace包的客戶端和服務(wù)端請求追蹤,默認開啟狀態(tài),可以查看事件和請求日志,幫助調(diào)試,具有一定的參考價值,感興趣的可以了解一下
    2025-03-03
  • 用Go語言標準庫實現(xiàn)Web服務(wù)之項目介紹

    用Go語言標準庫實現(xiàn)Web服務(wù)之項目介紹

    從本節(jié)開始將從后端到前端一步一步實現(xiàn)一個Go語言Web服務(wù),后端除了MySQL驅(qū)動,全部使用Go語言標準庫來實現(xiàn)一個小型項目,本篇將簡單的介紹一下項目開發(fā)要準備的流程,感興趣的同學可以閱讀一下
    2023-05-05
  • Go項目中添加生成時間與版本信息的方法

    Go項目中添加生成時間與版本信息的方法

    本文主要介紹了Go項目中添加生成時間與版本信息的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-04-04

最新評論

文昌市| 寿阳县| 济源市| 清水县| 金乡县| 兴安盟| 错那县| 克什克腾旗| 吉水县| 石林| 修水县| 广元市| 视频| 钟山县| 大埔区| 泰和县| 城口县| 馆陶县| 南昌市| 天台县| 沙河市| 永昌县| 随州市| 钟山县| 勃利县| 宜丰县| 湾仔区| 曲麻莱县| 三台县| 宜阳县| 姜堰市| 台北市| 汨罗市| 成都市| 凉山| 常州市| 满洲里市| 潮州市| 无为县| 万全县| 江阴市|