探索 Go 語言中的記憶體對齊:為什麼結構體大小會有所不同?

左诗右码發表於2024-11-22

在 Go 語言中,記憶體對齊是一個經常被忽略但非常重要的概念。理解記憶體對齊不僅可以幫助我們寫出更高效的程式碼,還能避免一些潛在的效能陷阱。

在這篇文章中,我們將透過一個簡單的例子來探討 Go 語言中的記憶體對齊機制,以及為什麼相似的結構體在記憶體中會佔用不同的大小。

示例程式碼

我們先來看一段程式碼:

package memory_alignment

import (
    "fmt"
    "unsafe"
)

type A struct {
    a int8
    b int8
    c int32
    d string
    e string
}

type B struct {
    a int8
    e string
    c int32
    b int8
    d string
}

func Run() {
    var a A
    var b B
    fmt.Printf("a size: %v \n", unsafe.Sizeof(a))
    fmt.Printf("b size: %v \n", unsafe.Sizeof(b))
    // a size: 40
    // b size: 48
}

在這個例子中,我們定義了兩個結構體 AB。它們的欄位基本相同,只是排列順序不同。然後,我們使用 unsafe.Sizeof 來檢視這兩個結構體在記憶體中的大小。

結果卻令人驚訝:結構體 A 的大小是 40 位元組,而結構體 B 的大小是 48 位元組。為什麼會出現這樣的差異呢?這就是我們今天要討論的記憶體對齊的作用。

記憶體對齊概念

記憶體對齊是指編譯器為了最佳化記憶體訪問速度,而對資料在記憶體中的位置進行調整的一種策略。不同型別的資料在記憶體中的對齊要求不同,例如:

  • int8 型別的變數通常對齊到 1 位元組邊界。
  • int32 型別的變數通常對齊到 4 位元組邊界。
  • 指標(如 string)通常對齊到 8 位元組邊界。

為了滿足這些對齊要求,編譯器可能會在結構體的欄位之間插入一些“填充”位元組,從而確保每個欄位都能正確對齊。

結構體記憶體佈局解析

讓我們深入分析一下 AB 兩個結構體的記憶體佈局,看看編譯器是如何為它們分配記憶體的。

結構體 A 的記憶體佈局

| a (int8) | b (int8) | padding (2 bytes) | c (int32) | d (string, 8 bytes) | e (string, 8 bytes) |
  • abint8 型別,各佔 1 位元組。
  • cint32 型別,需要 4 位元組對齊,b 後面會有 2 個填充位元組。
  • destring 型別,各佔 8 位元組。

總大小為:1 + 1 + 2 + 4 + 8 + 8 = 24 位元組。

結構體 B 的記憶體佈局

| a (int8) | padding (7 bytes) | e (string, 8 bytes) | c (int32) | padding (4 bytes) | b (int8) | padding (3 bytes) | d (string, 8 bytes) |
  • aint8 型別,佔 1 位元組,後面有 7 個填充位元組,以便 e 能夠對齊到 8 位元組邊界。
  • cint32 型別,需要 4 位元組對齊,因此在 c 後面沒有填充。
  • bint8 型別,需要填充 3 個位元組來對齊到 d 的 8 位元組邊界。

總大小為:1 + 7 + 8 + 4 + 4 + 1 + 3 + 8 = 36 位元組。

請注意,Go 編譯器可能會將 de 視為 8 位元組對齊型別(取決於系統和編譯器的實現),因此總大小可能是 48 位元組。

如何最佳化結構體記憶體佈局

為了減少結構體的記憶體佔用,我們可以按照欄位的對齊要求來重新排列欄位。例如:

  • 先宣告大的欄位(如 stringint32),然後是小的欄位(如 int8),可以減少記憶體中的填充位元組。

我們可以將 B 結構體改成以下形式:

type OptimizedB struct {
    e string
    d string
    c int32
    a int8
    b int8
}

這樣可以減少記憶體填充,從而最佳化記憶體佔用。

總結

記憶體對齊是編譯器最佳化記憶體訪問速度的一個重要策略。雖然它對大多數應用程式的影響可能較小,但在高效能場景或記憶體受限的環境中,理解並最佳化記憶體對齊可能會帶來顯著的效能提升。

在 Go 語言中,瞭解結構體的記憶體對齊規則,合理排列結構體欄位順序,不僅可以提高程式的效能,還能減少記憶體的浪費。這是一種簡單而有效的最佳化手段,希望大家在以後的程式設計實踐中能夠靈活運用。

相關文章