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

Go 泛型切片函數(shù)的內(nèi)存陷阱

 更新時間:2026年04月23日 15:35:39   作者:fliter  
本文主要介紹了Go 泛型切片函數(shù)的內(nèi)存陷阱,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

Go 1.21 引入了 slices 標準庫包,提供了一批操作切片的通用工具函數(shù)。但如果你不理解切片的底層內(nèi)存模型,很容易寫出看起來正確、實則存在內(nèi)存泄漏的代碼。本文結(jié)合 Go 官方博客,帶你把這件事徹底講清楚。

泛型讓切片函數(shù)寫一次就夠了

在泛型出現(xiàn)之前,如果你想實現(xiàn)一個"在切片中查找元素"的函數(shù),就得為每種類型各寫一份。有了類型參數(shù),只需寫一次:

// Index 返回 v 在 s 中第一次出現(xiàn)的下標,若不存在則返回 -1
func Index[S ~[]E, E comparable](s S, v E "S ~[]E, E comparable") int {
    for i := range s {
        if v == s[i] {
            return i
        }
    }
    return -1
}

slices 包正是基于這一思路,提供了 Clone、SortCompact、DeleteInsert、Replace 等大量通用函數(shù),覆蓋了日常操作切片的主要場景:

s := []string{"Bat", "Fox", "Owl", "Fox"}
s2 := slices.Clone(s)
slices.Sort(s2)
fmt.Println(s2) // [Bat Fox Fox Owl]
s2 = slices.Compact(s2)
fmt.Println(s2)                  // [Bat Fox Owl]
fmt.Println(slices.Equal(s, s2)) // false

先回顧切片的底層結(jié)構(gòu)

切片在 Go 內(nèi)部由三個字段構(gòu)成:指針(指向底層數(shù)組)、長度容量。兩個切片可以共享同一個底層數(shù)組,也可以指向數(shù)組的不同區(qū)段。

s := make([]T, 4, 6)

底層數(shù)組: [ e0 | e1 | e2 | e3 | -- | -- ]
                ↑
              s.ptr
s.len = 4, s.cap = 6

這個結(jié)構(gòu)決定了一件重要的事:如果一個函數(shù)需要改變切片的長度,它必須返回新的切片。這也是為什么 appendslices.Compact 有返回值,而 slices.Sort(只是重新排列元素)沒有返回值。

Delete 的實現(xiàn)原理

在泛型出現(xiàn)之前,從切片中刪除一段元素的慣用寫法是:

s = append(s[:2], s[5:]...)

語法繁瑣,極易寫錯。slices.Delete 把這件事封裝成了一行:

func Delete[S ~[]E, E any](s S, i, j int "S ~[]E, E any") S {
    return append(s[:i], s[j:]...)
}

其行為是:把 s[j:] 的元素向左移動,覆蓋掉 s[i:j],再返回長度縮短后的新切片。底層數(shù)組本身沒有重新分配,只是發(fā)生了元素的移動。

Go 1.22 之前的內(nèi)存泄漏問題

問題就藏在這里。

假設(shè)切片中存儲的是指針類型(比如 *Image),在刪除操作后,雖然新切片的長度縮短了,但底層數(shù)組尾部那些"超出長度"的位置,依然持有著原來的指針。

刪除前: [ p0 | p1 | p2 | p3 | p4 | p5 | -- | -- ]
調(diào)用 Delete(s, 2, 5) 后:
        [ p0 | p1 | p5 | p3 | p4 | p5 | -- | -- ]
                            ↑這里的指針沒有被清除
新切片長度為 3,但 p3、p4、p5 仍被底層數(shù)組引用

垃圾回收器無法釋放 p3、p4、p5 指向的對象,因為底層數(shù)組還"看得見"它們。如果這些指針指向的是幾十 MB 的大對象,就會造成顯著的內(nèi)存泄漏。

Go 1.22 的修復:自動清零尾部元素

Go 團隊在 Go 1.22 中修改了 Compact、CompactFunc、Delete、DeleteFunc、Replace 這五個函數(shù)的實現(xiàn),在操作完成后,用新增的內(nèi)置函數(shù) clear(Go 1.21 引入)將尾部多余的位置清零:

修復后,Delete(s, 2, 5) 的內(nèi)存狀態(tài):
[ p0 | p1 | p5 | nil | nil | nil | -- | -- ]
                      ↑ 已清零,GC 可以正?;厥?/p>

對于指針、切片、map、chan、interface 類型,零值就是 nil,GC 因此可以正?;厥者@些對象的內(nèi)存。

這個改動沒有修改任何 API,開發(fā)者無需更改代碼,內(nèi)存泄漏問題就自動消失了。

使用這些函數(shù)的常見錯誤

Go 1.22 的修復也帶來了一個副作用:之前一些"僥幸通過"的錯誤寫法,現(xiàn)在會在測試中暴露出來。以下是幾種典型錯誤:

錯誤一:忽略返回值

slices.Delete(s, 2, 3) // 錯誤!返回值被丟棄
// s 的長度沒變,但內(nèi)容已被修改,且尾部被置為 nil

錯誤二:對 Compact 也忽略返回值

slices.Sort(s)    // 正確
slices.Compact(s) // 錯誤!同樣需要接收返回值

錯誤三:把返回值賦給另一個變量,但繼續(xù)使用原切片

u := slices.Delete(s, 2, 3) // 之后還用 s?錯誤!
// s 的底層數(shù)組已被修改,尾部元素變成了 nil

錯誤四:用 := 而非 = 賦值,導致變量遮蔽

s := slices.Delete(s, 2, 3) // 注意:這里用了 :=
// 在某些作用域下,這會創(chuàng)建新變量,原來的 s 依然在外層作用域中被誤用

小結(jié)

slices 包是對 Go 切片操作的一次重要升級,泛型讓這些函數(shù)真正做到了"寫一次,處處可用"。

使用時記住兩件事:

  1. 凡是會改變切片長度的函數(shù)(Delete、Compact、Insert、Replace 等),都必須接收并使用它們的返回值,原切片在調(diào)用后應(yīng)視為無效。
  2. Go 1.22 已經(jīng)自動處理了尾部元素的內(nèi)存清零問題,你不再需要手動把多余的指針設(shè)為 nil,但前提是你正確地使用了返回值。

如果你的項目還在用 Go 1.21 或更早的版本,并且用到了 slices.Delete 等函數(shù)操作包含指針的切片,建議關(guān)注這個內(nèi)存泄漏問題,并考慮升級到 Go 1.22+。

參考資料

到此這篇關(guān)于Go 泛型切片函數(shù)的內(nèi)存陷阱的文章就介紹到這了,更多相關(guān)Go 泛型切片內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang基于JWT與Casbin身份驗證授權(quán)實例詳解

    Golang基于JWT與Casbin身份驗證授權(quán)實例詳解

    這篇文章主要為大家介紹了Golang基于JWT與Casbin實現(xiàn)身份驗證授權(quán)實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • go?colly?爬蟲實現(xiàn)示例

    go?colly?爬蟲實現(xiàn)示例

    這篇文章主要為大家介紹了go?colly?爬蟲實現(xiàn)示例,效果是根據(jù)輸入的瀏覽器cookie及excel必要行列號,從excel中讀取公司名稱,查詢公司法人及電話號碼。并寫回到excel中指定行
    2022-09-09
  • golang string、int、int64 float 互相轉(zhuǎn)換方式

    golang string、int、int64 float 互相轉(zhuǎn)換方式

    這篇文章主要介紹了golang string、int、int64 float 互相轉(zhuǎn)換方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • GO語言中的Map使用方法詳解

    GO語言中的Map使用方法詳解

    這篇文章主要給大家介紹了關(guān)于GO語言中Map使用方法的相關(guān)資料,在go語言中map是散列表的引用,map的類型是map[k]v,也就是常說的k-v鍵值對,需要的朋友可以參考下
    2023-08-08
  • golang進行簡單權(quán)限認證的實現(xiàn)

    golang進行簡單權(quán)限認證的實現(xiàn)

    本文主要介紹了golang簡單權(quán)限認證的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • golang為什么要統(tǒng)一錯誤處理

    golang為什么要統(tǒng)一錯誤處理

    這篇文章主要介紹了golang為什么要統(tǒng)一錯誤處理,統(tǒng)一錯誤處理的目的是為了前端開發(fā)接收到后端的statuscode,之后便于前端邏輯上開發(fā)以及開發(fā),下文具體操作過程需要的小伙伴可以參考一下
    2022-04-04
  • golang判斷key是否在map中的代碼

    golang判斷key是否在map中的代碼

    這篇文章主要介紹了golang判斷key是否在map中的代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Go實現(xiàn)數(shù)據(jù)脫敏的方案設(shè)計

    Go實現(xiàn)數(shù)據(jù)脫敏的方案設(shè)計

    在一些常見的業(yè)務(wù)場景中可能涉及到用戶的手機號,銀行卡號等敏感數(shù)據(jù),對于這部分的數(shù)據(jù)經(jīng)常需要進行數(shù)據(jù)脫敏處理,就是將此部分數(shù)據(jù)隱私化,防止數(shù)據(jù)泄露,所以本文給大家介紹了Go實現(xiàn)數(shù)據(jù)脫敏的方案設(shè)計,需要的朋友可以參考下
    2024-05-05
  • golang使用redis實現(xiàn)全文搜索功能詳解

    golang使用redis實現(xiàn)全文搜索功能詳解

    這篇文章主要為大家詳細介紹了golang如何使用redis實現(xiàn)全文搜索功能,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2025-02-02
  • Go通道channel通過通信共享內(nèi)存

    Go通道channel通過通信共享內(nèi)存

    這篇文章主要為大家介紹了Go通道channel通過通信共享內(nèi)存示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07

最新評論

陕西省| 游戏| 大新县| 凤山市| 普兰店市| 分宜县| 镇赉县| 调兵山市| 宽城| 天柱县| 洪江市| 洪泽县| 家居| 绿春县| 江川县| 古浪县| 松滋市| 阿巴嘎旗| 淳化县| 德格县| 祁门县| 永登县| 南宁市| 任丘市| 永新县| 左权县| 湘阴县| 本溪市| 垫江县| 射阳县| 泰和县| 巴南区| 万宁市| 丰原市| 平阳县| 永丰县| 胶州市| 高要市| 张家界市| 墨竹工卡县| 苍山县|