日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

Go 語言中的map和內(nèi)存泄漏

來源: 責(zé)編: 時間:2023-11-21 17:13:42 393觀看
導(dǎo)讀Map在內(nèi)存中總是會增長;它不會收縮。因此,如果map導(dǎo)致了一些內(nèi)存問題,你可以嘗試不同的選項,比如強制 Go 重新創(chuàng)建map或使用指針。在 Go 中使用map時,我們需要了解map增長和收縮的一些重要特性。讓我們深入探討這一點,以防

Map在內(nèi)存中總是會增長;它不會收縮。因此,如果map導(dǎo)致了一些內(nèi)存問題,你可以嘗試不同的選項,比如強制 Go 重新創(chuàng)建map或使用指針。gYm28資訊網(wǎng)——每日最新資訊28at.com

gYm28資訊網(wǎng)——每日最新資訊28at.com

在 Go 中使用map時,我們需要了解map增長和收縮的一些重要特性。讓我們深入探討這一點,以防止可能導(dǎo)致內(nèi)存泄漏的問題。gYm28資訊網(wǎng)——每日最新資訊28at.com

首先,為了查看這個問題的一個具體例子,讓我們設(shè)計一個場景,在這個場景中我們將使用以下map:gYm28資訊網(wǎng)——每日最新資訊28at.com

m := make(map[int][128]byte)

每個 m 的值都是一個包含 128 字節(jié)的數(shù)組。我們將執(zhí)行以下操作:gYm28資訊網(wǎng)——每日最新資訊28at.com

  • 分配一個空的map。
  • 添加 100 萬個元素。
  • 刪除所有元素,并運行垃圾回收(GC)。

在每個步驟之后,我們希望打印堆的大小(使用一個 printAlloc 實用函數(shù))。這將展示這個示例在內(nèi)存方面的行為方式:gYm28資訊網(wǎng)——每日最新資訊28at.com

func main() {    n := 1_000_000    m := make(map[int][128]byte)    printAlloc()    for i := 0; i < n; i++ { // Adds 1 million elements        m[i] = [128]byte{}    }    printAlloc()    for i := 0; i < n; i++ { // Deletes 1 million elements        delete(m, i)    }    runtime.GC() // Triggers a manual GC    printAlloc()    runtime.KeepAlive(m) // Keeps a reference to m so that the map isn’t collected}func printAlloc() {    var m runtime.MemStats    runtime.ReadMemStats(&m)    fmt.Printf("%d KB/n", m.Alloc/1024)}

我們分配一個空的map,添加 100 萬個元素,刪除 100 萬個元素,然后運行垃圾回收。我們還確保使用 runtime.KeepAlive 保持對map的引用,以防止map被收集。讓我們運行這個示例:gYm28資訊網(wǎng)——每日最新資訊28at.com

0 MB   <-- After m is allocated461 MB <-- After we add 1 million elements293 MB <-- After we remove 1 million elements

我們觀察到了什么?起初,堆大小很小。然后,在將 100 萬個元素添加到map后,它顯著增長了。但是,如果我們期望在刪除所有元素后堆大小會減小,這并不是 Go 中map的工作方式。最后,盡管 GC 已經(jīng)收集了所有元素,但堆大小仍然是 293 MB。因此,內(nèi)存縮小了,但并非我們可能預(yù)期的方式。這其中的原理是什么?我們需要深入了解一下 Go 中map的工作原理。gYm28資訊網(wǎng)——每日最新資訊28at.com

map提供了一個無序的鍵值對集合,其中所有的鍵都是唯一的。在 Go 中,map基于哈希表數(shù)據(jù)結(jié)構(gòu):一個數(shù)組,其中每個元素都是指向鍵值對存儲桶的指針,如圖1所示。gYm28資訊網(wǎng)——每日最新資訊28at.com

gYm28資訊網(wǎng)——每日最新資訊28at.com

圖1 — 哈希表示例,重點關(guān)注存儲桶 0。gYm28資訊網(wǎng)——每日最新資訊28at.com

每個存儲桶都是一個固定大小的數(shù)組,包含八個元素。如果要將元素插入已經(jīng)滿了的存儲桶(即存儲桶溢出),Go 會創(chuàng)建另一個包含八個元素的存儲桶,并將前一個存儲桶鏈接到它上。圖2顯示了一個例子:gYm28資訊網(wǎng)——每日最新資訊28at.com

gYm28資訊網(wǎng)——每日最新資訊28at.com

圖2 — 如果存儲桶溢出,Go 會分配一個新的存儲桶,并將前一個存儲桶鏈接到它上。gYm28資訊網(wǎng)——每日最新資訊28at.com

在底層,Go 中的map是指向 runtime.hmap 結(jié)構(gòu)體的指針。該結(jié)構(gòu)體包含多個字段,其中包括一個 B 字段,表示map中存儲桶的數(shù)量:gYm28資訊網(wǎng)——每日最新資訊28at.com

type hmap struct {    B uint8 // log_2 of # of buckets            // (can hold up to loadFactor * 2^B items)    // ...}

在添加了100萬個元素之后,B 的值等于18,這意味著有 21? = 262,144 個存儲桶。當我們刪除了100萬個元素后,B 的值是多少呢?仍然是18。因此,map仍然包含相同數(shù)量的存儲桶。gYm28資訊網(wǎng)——每日最新資訊28at.com

原因在于map中存儲桶的數(shù)量是不可縮減的。因此,從map中刪除元素不會影響現(xiàn)有存儲桶的數(shù)量;它只是將存儲桶中的槽清零。map只能增長并擁有更多的存儲桶;它永遠不會縮小。gYm28資訊網(wǎng)——每日最新資訊28at.com

在先前的示例中,我們從461 MB減少到了293 MB,因為元素被收集,但運行垃圾回收并沒有影響map本身。即使額外存儲桶的數(shù)量(因為溢出而創(chuàng)建的存儲桶)也保持不變。gYm28資訊網(wǎng)——每日最新資訊28at.com

讓我們退一步,討論map無法縮小的情況何時可能成為問題。想象一下使用 map[int][128]byte 來構(gòu)建緩存。這個map以每個客戶ID(int)為鍵,保存一個長度為128字節(jié)的序列。現(xiàn)在,假設(shè)我們想保存最近的1000位客戶。map的大小將保持不變,所以我們不必擔(dān)心map無法縮小的問題。gYm28資訊網(wǎng)——每日最新資訊28at.com

但是,假設(shè)我們想要存儲一小時的數(shù)據(jù)。同時,我們的公司決定在黑色星期五進行大促銷:在一個小時內(nèi),我們可能會有數(shù)百萬的客戶連接到我們的系統(tǒng)。但是在黑色星期五之后的幾天,我們的map將包含與高峰期相同數(shù)量的存儲桶。這就解釋了為什么在這種情況下我們可能會遇到內(nèi)存消耗高卻不會顯著減少的情況。gYm28資訊網(wǎng)——每日最新資訊28at.com

如果我們不想手動重啟服務(wù)來清理map消耗的內(nèi)存量,有哪些解決方案?一種解決方案可以是定期重新創(chuàng)建當前map的副本。例如,每小時我們可以構(gòu)建一個新map,復(fù)制所有元素,并釋放先前的map。這種選擇的主要缺點是,在復(fù)制后直到下一次垃圾回收之前,我們可能會在短時間內(nèi)消耗兩倍于當前內(nèi)存。gYm28資訊網(wǎng)——每日最新資訊28at.com

另一種解決方案是將map類型更改為存儲數(shù)組指針:map[int]*[128]byte。這并沒有解決我們會有大量存儲桶的問題;然而,每個存儲桶條目將為值保留指針的大小,而不是128字節(jié)(64位系統(tǒng)上為8字節(jié),32位系統(tǒng)上為4字節(jié))。gYm28資訊網(wǎng)——每日最新資訊28at.com

回到原始場景,讓我們比較每種map類型在每個步驟后的內(nèi)存消耗。以下表格顯示了比較。gYm28資訊網(wǎng)——每日最新資訊28at.com

Step
gYm28資訊網(wǎng)——每日最新資訊28at.com

map[int][128]bytegYm28資訊網(wǎng)——每日最新資訊28at.com

map[int]*[128]bytegYm28資訊網(wǎng)——每日最新資訊28at.com

分配一個空的 map
gYm28資訊網(wǎng)——每日最新資訊28at.com

0 MB
gYm28資訊網(wǎng)——每日最新資訊28at.com

0 MB
gYm28資訊網(wǎng)——每日最新資訊28at.com

添加100萬個元素
gYm28資訊網(wǎng)——每日最新資訊28at.com

461 MB
gYm28資訊網(wǎng)——每日最新資訊28at.com

182 MB
gYm28資訊網(wǎng)——每日最新資訊28at.com

刪除所有元素并運行GC
gYm28資訊網(wǎng)——每日最新資訊28at.com

293 MB
gYm28資訊網(wǎng)——每日最新資訊28at.com

38 MB
gYm28資訊網(wǎng)——每日最新資訊28at.com

正如我們所看到的,在刪除所有元素后,使用 map[int]*[128]byte 類型所需的內(nèi)存量明顯較少。此外,在這種情況下,由于一些優(yōu)化措施以減少內(nèi)存消耗,高峰時期所需的內(nèi)存量也較少顯著。gYm28資訊網(wǎng)——每日最新資訊28at.com

注意:如果鍵或值超過128字節(jié),Go 將不會直接將其存儲在map存儲桶中。相反,Go 將存儲用于引用鍵或值的指針。gYm28資訊網(wǎng)——每日最新資訊28at.com

結(jié)論

正如我們所見,向map添加 n 個元素,然后刪除所有元素意味著在內(nèi)存中保持相同數(shù)量的存儲桶。因此,我們必須記住,由于 Go map只能增長,因此其內(nèi)存消耗也會隨之增加。它沒有自動化的策略來縮小。如果這導(dǎo)致內(nèi)存消耗過高,我們可以嘗試不同的選項,比如強制 Go 重新創(chuàng)建map或使用指針來檢查是否可以進行優(yōu)化。gYm28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-32441-0.htmlGo 語言中的map和內(nèi)存泄漏

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: C語言代碼:數(shù)字雨

下一篇: 五種在 JavaScript 中創(chuàng)建對象的方法

標簽:
  • 熱門焦點
Top 主站蜘蛛池模板: 苏州市| 岳西县| 浮山县| 古浪县| 松溪县| 古蔺县| 大城县| 吴忠市| 灵台县| 浠水县| 霞浦县| 瓦房店市| 沽源县| 德江县| 洪雅县| 甘南县| 寿光市| 陵川县| 阿鲁科尔沁旗| 黑山县| 南投县| 巨野县| 剑阁县| 象山县| 得荣县| 林口县| 甘泉县| 德钦县| 建宁县| 陈巴尔虎旗| 瑞安市| 梨树县| 玉门市| 旬邑县| 菏泽市| 句容市| 宁波市| 横峰县| 奎屯市| 电白县| 五家渠市|