內存對齊、內存佈局是怎麼回事

字長(word size)

字長(word size),指的是 CPU 一次可以訪問數據的最大長度:

  • 對於 32 位的 cpu 來說:word size 爲 2^32,即 4 byte
  • 對於 64 位的 cpu 來說:word size 爲 2^64,即 8 byte

兩種內存佈局

下邊是一個結構體的兩種內存佈局方式

第一種:按順序

代碼

type Foo struct {
    A int8 // 1
    B int8 // 1
    C int8 // 1
}

type Bar struct {
    x int32 // 4
    y *Foo  // 8
    z bool  // 1
}
Go
Copy

你覺得 Bar 對象會佔用多少的內存? 可能很多人會下意識地回答 13

會回答 13 是因爲你覺得該結構體的內存分配是下面這樣按順利分配的。

按照前面所介紹的 word size 爲 8 來計算,使用這種分配方式,當你訪問 bar.y 的時候,CPU 需要訪問內存兩次。

memory layout of Bar1

第二種:按字長

而如果使用下面這種方式,當你再次訪問 bar.y 的時候,CPU 需要訪問內存一次。

memory layout of Bar1

因此真正的答案是 24,這是一種典型的用空間換時間的方法 — 內存對齊

func main() {
    var bar Bar
    fmt.Println(unsafe.Sizeof(bar))  // 24
}
 

合理定義結構體

從以上可以發現,我們定義的結構體雖然不大,只佔用 24個byte,但實際有用的只有 13 的byte,內存使用率只有 50% 左右,很有優化的必要性。

如果將第三個屬性挪 y 的前面

memory layout of Bar2

那就可以省下來 1 個 byte 了

func main() {
    var bar Bar
    fmt.Println(unsafe.Sizeof(bar))  // 16
}
 

y 爲什麼佔用 8 字節?

看完了上面的介紹,想必你一定有一個疑問: Foo 結構體實際佔用 3個byte,爲什麼 Bar.y 卻要佔用 8個 byte 呢?

type Foo struct {
    A int8 // 1
    B int8 // 1
    C int8 // 1
}

type Bar struct {
    x int32 // 4
    y *Foo  // 8
    z bool  // 1
}
 

因爲 Bar.y 表示的是一個指針,而指針的對齊係數是 8

func main() {
    var bar Bar
    fmt.Println(unsafe.Alignof(bar.y))  // 8
}

你大可將 y 改成普通對象

type Foo struct {
    A int8 // 1
    B int8 // 1
    C int8 // 1
}

type Bar struct {
    x int32 // 4
    y Foo  // 3
    z bool  // 1
}

這樣一來,bar 對象就只佔用一個字長

func main() {
    var bar Bar
    fmt.Println(unsafe.Sizeof(bar)) // 8
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章