字長(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
}
你覺得 Bar 對象會佔用多少的內存? 可能很多人會下意識地回答 13
會回答 13 是因爲你覺得該結構體的內存分配是下面這樣按順利分配的。
按照前面所介紹的 word size 爲 8 來計算,使用這種分配方式,當你訪問 bar.y 的時候,CPU 需要訪問內存兩次。
第二種:按字長
而如果使用下面這種方式,當你再次訪問 bar.y 的時候,CPU 需要訪問內存一次。
因此真正的答案是 24,這是一種典型的用空間換時間的方法 — 內存對齊
func main() {
var bar Bar
fmt.Println(unsafe.Sizeof(bar)) // 24
}
合理定義結構體
從以上可以發現,我們定義的結構體雖然不大,只佔用 24個byte,但實際有用的只有 13 的byte,內存使用率只有 50% 左右,很有優化的必要性。
如果將第三個屬性挪 y
的前面
那就可以省下來 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
}