首頁 >後端開發 >Golang >為什麼 Go 對於長度為 100k 的切片使用的記憶體比長度為 100k 的陣列少?

為什麼 Go 對於長度為 100k 的切片使用的記憶體比長度為 100k 的陣列少?

王林
王林轉載
2024-02-09 10:12:09503瀏覽

为什么 Go 对于长度为 100k 的切片使用的内存比长度为 100k 的数组要少?

Go語言在處理切片和陣列時,對於長度為100k的切片所使用的記憶體要比長度為100k的陣列要少。這是因為切片在底層實作中採用了指標和長度的組合,而陣列則需要連續的記憶體空間來儲存資料。由於切片的長度是可變的,所以可以動態地分配和釋放內存,而數組則需要在聲明時就指定固定的長度。因此,當處理大量資料時,使用切片可以更有效率地利用記憶體空間,減少記憶體的佔用。這也是Go語言在處理大規模資料時的一個優勢之一。

問題內容

考慮以下程式碼,我分配了 4000 個數組,每個數組長度為 100k:

parentmap := make(map[int][100_000]int)
    for i := 0; i < 4000; i++ {
        parentmap[i] = [100_000]int{}
        time.sleep(3 * time.millisecond)
    }

如果我在本地運行該程式並分析其記憶體使用情況,它會開始使用 >2gb 記憶體。

現在,如果我們稍微更改程式碼以使用陣列切片(但長度也為 100k),如下所示:

parentMap := make(map[int][]int)
    for i := 0; i < 4000; i++ {
        parentMap[i] = make([]int, 100_000)
        time.Sleep(3 * time.Millisecond)
    }

在我的機器上,記憶體峰值約為 73mb。 這是為什麼?

我認為這兩個片段將使用大致相同的內存,原因如下:

  • 在這兩種情況下,go 運行時都會在堆上分配 parentmap 的值。 go 這樣做是因為如果它在堆疊上分配這些值,那麼一旦當前函數超出範圍,parentmap 的值就會全部清除。
  • 因此第一個代碼段直接在堆上分配 4k 數組。
  • 並且,第二個片段在堆上分配 4k 切片標頭。每個切片頭都有一個指向大小為 100k 的唯一數組(也在堆上)的指標。
  • 在這兩種情況下,大小為 100k 的堆上都有 4k 陣列。因此,在任何一種情況下都應使用大致相等的內存量。

我讀:https://go.dev/blog/slices-intro。但找不到解釋這一點的實作細節。

解決方法

有切片的版本可能會受益於延遲分配。沒有任何東西會嘗試寫入這些片之一的資料緩衝區,因此作業系統可以自由地不為這些緩衝區實際分配內存,直到確實嘗試寫入。 (作業系統也可以延遲對緩衝區進行零初始化,因此不會強制分配。)

同時,帶有陣列的版本需要將陣列實際複製到映射中,這意味著實際執行寫入。即使寫入的值全為零,它們仍然是寫入,因此作業系統必須實際為要寫入的資料分配記憶體。

嘗試將資料寫入這些切片,切片版本也應該佔用千兆位元組的記憶體。 (我認為每頁記憶體一個值應該足夠了,但是用 1s 填充切片可能會更容易。)

以上是為什麼 Go 對於長度為 100k 的切片使用的記憶體比長度為 100k 的陣列少?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除