歡迎關(guān)注微信公眾號(hào):全棧工廠
1. 先看一個(gè)問題
請思考30秒想想以下代碼輸出的內(nèi)容是多少闸准?
package main
import (
"fmt"
"unsafe"
)
type S1 struct {
A byte
B int64
C byte
}
type S2 struct {
A byte
C byte
B int64
}
func main() {
s1 := S1{}
fmt.Printf("S1.A size: %d\n", unsafe.Sizeof(s1.A))
fmt.Printf("S1.B size: %d\n", unsafe.Sizeof(s1.B))
fmt.Printf("S1.C size: %d\n", unsafe.Sizeof(s1.C))
fmt.Printf("S1 size: %d\n\n", unsafe.Sizeof(s1))
s2 := S2{}
fmt.Printf("S2.A size: %d\n", unsafe.Sizeof(s2.A))
fmt.Printf("S2.B size: %d\n", unsafe.Sizeof(s2.B))
fmt.Printf("S2.C size: %d\n", unsafe.Sizeof(s2.C))
fmt.Printf("S2 size: %d\n", unsafe.Sizeof(s2))
}
執(zhí)行后代碼輸出:
S1.A size: 1
S1.B size: 8
S1.C size: 1
S1 size: 24
S2.A size: 1
S2.B size: 8
S2.C size: 1
S2 size: 16
你或許會(huì)疑問,為什么S1和S2所占存儲(chǔ)空間不僅不是A锭汛、B秃臣、C總和10而且S1和S2所占空間也各不一樣?問題的關(guān)鍵就在于"內(nèi)存對齊"淮蜈。
2. 什么是內(nèi)存對齊斋攀?
在計(jì)算機(jī)中,CPU通過特定的指令從內(nèi)存中讀取數(shù)據(jù)梧田,由于CPU訪問內(nèi)存已得到數(shù)據(jù)的時(shí)間要比執(zhí)行指令花費(fèi)的時(shí)間多得多淳蔼,因此在CPU內(nèi)部提供了一些通用寄存器用來暫存從內(nèi)存中加載到的數(shù)據(jù),CPU一次讀取的數(shù)據(jù)量為一個(gè)字裁眯,字的位數(shù)我們稱之為字長鹉梨,因此字長的大小直接決定了CPU一次能處理的數(shù)據(jù)量的大小穿稳;一般情況下存皂,字長越大,CPU性能越高逢艘,我們熟知的64位計(jì)算機(jī)就表示CPU一次能處理的數(shù)據(jù)量為64位即8字節(jié)艰垂。
因此,所謂內(nèi)存對齊就是計(jì)算機(jī)將內(nèi)存中的數(shù)據(jù)按照一個(gè)字的長度(即:CPU數(shù)據(jù)處理單位)進(jìn)行對齊埋虹,保證CPU以高效的方式精確讀取到所需要的數(shù)據(jù)。
3. 為什么要內(nèi)存對齊娩怎?
3.1 提高性能
如果CPU不按照塊(字)去讀取數(shù)據(jù)搔课,而是按照字節(jié)去讀取數(shù)據(jù),那么CPU讀取一個(gè)int64值就需要讀取8次截亦,效率很低爬泥,所以最終CPU被設(shè)計(jì)一次讀取一個(gè)字長的數(shù)據(jù),內(nèi)存的基本存儲(chǔ)結(jié)構(gòu)如下:
如果不進(jìn)行內(nèi)存對齊的話崩瓤,一個(gè)int64在內(nèi)存中的存儲(chǔ)位置很可能像下面這樣:
這樣的話袍啡,CPU就需要讀取兩次才能完成這個(gè)int64值的讀取操作,而進(jìn)行內(nèi)存對齊后却桶,保證每個(gè)變量的值都存儲(chǔ)在整數(shù)倍機(jī)器字長的內(nèi)存地址上境输,最小化CPU內(nèi)存讀取次數(shù)蔗牡,提高效率。
3.2 避免出錯(cuò)
不是所有的硬件平臺(tái)都能訪問任意地址上的任意數(shù)據(jù)的嗅剖;某些硬件平臺(tái)只能在某些地址處取某些特定類型的數(shù)據(jù)辩越,否則拋出硬件異常,出現(xiàn)錯(cuò)誤信粮。
4. 問題解答
了解內(nèi)存對齊后黔攒,我們再看文章開始我們說道的那題,在我的64位計(jì)算機(jī)環(huán)境中强缘,S1和S2在內(nèi)存中的大致位置如下圖所示:
所以督惰,最后S1所占的內(nèi)存空間為24字節(jié),S2所占空間為16字節(jié)旅掂。