cpu要想從內存讀取數據,需要通過地址總線,把地址傳輸給內存,內存准備好數據,輸出到數據總線,交給cpu,如果地址總線只有8根,那這個地址就只有8位可以表示[0,255]256個地址,因為表示不了更多的地址,就用不了更大的內存,所以256就是8根地址總線最大的尋址空間,要使用更大的內存,就要有更寬的地址總線,例如32位地址總線就可以尋址4G內存了,每次操作1字節太慢,那就加寬數據總線,要想每次操作4字節,就要至少32位數據總線,8字節就要64位,這里每次擦操作的字節數,就是所謂的機器字長。
如果內存就像我們邏輯上認為的那樣,一個挨着一個形成這樣的大矩陣,我們可以訪問任意地址,並把他輸出到總線,但是實際上為了實現更高的訪問效率,典型的內存布局是這樣的,一個內存條的一面是一個rank,黑色是一個chip,一個chip包括8個banks,到bank這里就可以通過選擇行選擇列來定位一個地址了。
這不像是我們邏輯上認為的那樣連續的存在,但是他們公用同一個地址,各自選擇同一個位置的一個字節,再組合起來作為我們邏輯上認為的連續8個字節,通過這樣的並行操作,提高了內存訪問效率,但是如果使用這種設計,這里的地址就只能是8的倍數,如果非要錯開一個格,由於最后一個字節對應位置與前七個不同,不能在一次操作中被同一個地址選中,所以這樣的地址是不能用的。
之所以有些CPU能夠支持訪問任意地址,是因為它多做了許多處理,比如你想從地址1開始讀8字節的數據CPU會分兩次讀,第一次從0-7,但只取后7字節,第二次從8-15,但只取第1字節,把兩次結果拼起來拿到所需數據,但是這必然會影響性能,所以為了保證程序順利高效的運行,編譯器會把各種類型的數據安排到合適的地址並占用合適的長度,這就是內存對齊,每種類型的對齊值就是它的對齊邊界,
內存對齊要求數據存起始儲地址以及占用字節數都要是它對齊邊界的倍數,所以這個int32要錯開兩個字節,從4開始存卻不能緊接着從2開始。
現在的問題是怎么確定每種類型的對齊邊界呢,這和平台有關。
go語言支持這些平台,可以看到,指針寬度和寄存器寬度都是4字節,64位平台上都是8字節,而被go語言稱為寄存器寬度的這個值,就可以理解位機器字長,也是平台對應的最大對齊邊界 。
而數據類型的對齊邊界是取數據類型大小和平台對齊邊界中較小的那個,不過要注意,同一個類型在不同平台上的大小可能不同,對齊邊界也可能不同
為什么不統一使用平台對齊邊界或者統一按數據類型大小來對齊呢?我們來試一下:
假設現在是64位平台,最大對齊邊界是8字節,int8只有一個字節,按照1字節對齊的話,它可以放在任何位置,因為總能通過一次讀取把它完整拿出來,如果統一對齊到8字節,雖然同樣只要讀取一次,但是每次讀取都要浪費7個字節,所以對齊到1可以節省內存空間。
int16占2字節,按照2字節對齊,可以從這些地址開始存,而且能保證只用讀取一次
如果按照1字節對齊就可能存成這樣,那就要讀取2次(0-7最后一個,8-15第一個)再截取拼接,會影響性能
如果對齊到8字節,與int8一樣,浪費6個內存空間,所以對齊到2
這是小於最大對齊邊界的情況,如果大於最大對齊邊界呢?
假設32位平台存儲int64類型的數據,(此時我們取的對齊邊界是4bytes)
當0和1被占用的情況下,如果類型對齊大小為8,就要從這里開始存
如果對齊到4,就可以從這里開始,內存浪費更少
所以內存對齊邊界會這樣選擇依然是為了
減少浪費,提高性能
最后,來看看怎么確定一個結構體的對齊邊界
對結構體而言,首先要確定每個成員的對齊邊界,然后取其中最大的,這就是這個結構體類型的對齊邊界
然后我們來存儲這個結構體變量,
內存對齊第一個要求,存儲這個結構體的起始地址,是對齊邊界的倍數,假設從這里開始存
結構體的每個成員在存儲時,都要把這個起始地址當作地址0,然后再用相對地址來決定自己該放在哪兒
來看第一個成員a,他要對齊1字節,而這里是相對地址0,所以直接放在這
接下來是成員b,他要對齊8字節,但是接下來的相對地址1對8取模並不等於0,所以要往后挪,b放這里
接下來是c,它要對齊4字節,直接放在這里就行
最后是d,對齊到2字節,所以放這兒
所有成員都放好還不算完,別忘了
內存對齊的第二個要求,結構體整體占用字節數需要是類型對齊邊界的倍數,不夠的話需要往后擴張一下,所以他要擴充到相對地址23這里(24%8=0),最終,這個結構體的類型大小就是24字節。
至於為什么要限制類型大小等於其對齊邊界的整數倍,我們可以這樣理解
如果這里不擴張到整數倍,則這個結構體的類型大小為22字節,那么要使用一個長度為2的T類型數組按照元素類型大小會占用44字節的內存,問題出現了
第二個元素並沒有內存對齊(22%8 !=0)
所以,只有結構體的大小是對齊邊界的整數倍,才能保證數組中每個元素的內存都是對齊的。
注:本文總結自B站UP主
幼麟實驗室的視頻,只為方便復習使用。