弄明白什麼是內存對齊的時候,先來看一個demohtml
type s struct { Bool bool Byte byte In32 int32 Int64 int64 Int8 int8 } func main() { fmt.Printf("bool size: %d\n", unsafe.Sizeof(bool(true))) fmt.Printf("int32 size: %d\n", unsafe.Sizeof(int32(0))) fmt.Printf("int8 size: %d\n", unsafe.Sizeof(int8(0))) fmt.Printf("int64 size: %d\n", unsafe.Sizeof(int64(0))) fmt.Printf("byte size: %d\n", unsafe.Sizeof(byte(0))) fmt.Printf("string size: %d\n", unsafe.Sizeof("E")) part1 := s{} fmt.Printf("part1 size: %d, align: %d\n", unsafe.Sizeof(part1), unsafe.Alignof(part1)) }
打印下輸出git
bool size: 1 int32 size: 4 int8 size: 1 int64 size: 8 byte size: 1 string size: 16 part1 size: 24, align: 8
按照不能類型的長度計算,結構體s
的長度應該是1+1+4+8+1=15
,可是咱們經過unsafe.Sizeof()
計算出來的長度是24。這就是發生了內存對齊形成的。golang
現代計算機中內存空間都是按照byte
劃分的,從理論上講彷佛對任何類型的變量的訪問能夠從任何地址開始,但實際狀況是在訪問特定類型變量的時候常常在特定的內存地址訪問, 這就須要各類類型數據按照必定的規則在空間上排列,而不是順序的一個接一個的排放,這就是內存對齊。數組
一、平臺緣由(移植緣由):不是全部的硬件平臺都能訪問任意地址上的任意數據的;某些硬件平臺只能在某些地址處取某些特定類型的數據,不然拋出硬件異常。數據結構
二、性能緣由:數據結構(尤爲是棧)應該儘量地在天然邊界上對齊。緣由在於,爲了訪問未對齊的內存,處理器須要做兩次內存訪問;而對齊的內存訪問僅須要一次訪問。佈局
咱們認爲的內存空間多是簡單的字節數組post
可是處理器對待內存不是這樣的,CPU把內存看成一塊一塊的,塊的大小能夠是二、四、八、16字節大小,所以CPU讀取內存是一塊一塊讀取的。(塊的大小稱爲內存讀取粒度)性能
若是沒有內存對齊:.net
假設當前內存讀取粒度爲43d
假如沒有內存對齊機制,數據能夠任意存放,如今一個int變量存放在從地址1開始的連續四個字節地址中,該處理器去取數據時,要先從0地址開始讀取第一個4字節塊,剔除不想要的字節(0地址),而後從地址4開始讀取下一個4字節塊,一樣剔除不要的數據(5,6,7地址),最後留下的兩塊數據合併放入寄存器.這須要作不少工做。
若是內存對齊了:
如今有了內存對齊的,int類型數據只能存放在按照對齊規則的內存中,好比說0地址開始的內存。那麼如今該處理器在取數據時一次性就能將數據讀出來了,並且不須要作額外的操做,提升了效率。
若是地址未對齊,則至少須要兩次內存訪問。可是,若是所需數據跨越虛擬內存的兩頁會發生什麼?這可能致使駐留第一頁而沒有駐留最後一頁的狀況。訪問時,在指令中間,將產生頁面錯誤,執行虛擬內存管理交換代碼,從而破壞指令的原子性。
合理的內存對齊能夠提升內存讀寫的性能,而且便於實現變量操做的原子性。
在不一樣平臺上的編譯器都有本身默認的 「對齊係數」,可經過預編譯命令#pragma pack(n)
進行變動,n就是代指 「對齊係數」。
通常來說,咱們經常使用的平臺的係數以下:
32 位:4
64 位:8
查看幾種類型的對齊係數
func main() { fmt.Printf("bool align: %d\n", unsafe.Alignof(bool(true))) fmt.Printf("int32 align: %d\n", unsafe.Alignof(int32(0))) fmt.Printf("int8 align: %d\n", unsafe.Alignof(int8(0))) fmt.Printf("int64 align: %d\n", unsafe.Alignof(int64(0))) fmt.Printf("byte align: %d\n", unsafe.Alignof(byte(0))) fmt.Printf("string align: %d\n", unsafe.Alignof("test")) fmt.Printf("map align: %d\n", unsafe.Alignof(map[string]string{})) }
對齊係數
bool align: 1 int32 align: 4 int8 align: 1 int64 align: 8 byte align: 1 string align: 8 map align: 8
不一樣的平臺對齊係數可能不一樣,根據電腦的實際狀況作分析
一、對於結構的各個成員,第一個成員位於偏移爲0的位置,之後每一個數據成員的起始位置必須是默認對齊長度和該數據成員長度中最小的長度的倍數。
二、除告終構成員須要對齊,結構自己也須要對齊,結構的長度必須是編譯器默認的對齊長度和成員中最長類型中最小的數據大小的倍數對齊。
咱們根據上面的例子具體的分析下
type s struct { Bool bool Byte byte In32 int32 Int64 int64 Int8 int8 }
對齊流程
Bool
Byte
大小/對齊值
1,因此不用paddingIn32
大小/對齊值
4,可是結構內存起始偏移量是2,2不是4的最小倍數,因此要padding空間爲2Int64
大小/對齊值
8,當前結構內存起始偏移量是8,因此不用paddingInt8
大小/對齊值
4,當前結構內存起始偏移量是16,因此不用padding內存對齊主要的依據就是上面兩個對齊的規則,數據成員的起始位置必須是默認對齊長度和該數據成員長度中最小的長度的倍數,同時對於整個結構對象,最終的地址要是編譯器默認的對齊長度和成員中最長類型中最小的數據大小的倍數。
一、內存對齊是不可缺乏的,可以減小cpu的訪問內存的次數;
二、合理的內存佈局也便於實現變量操做的原子性;
三、不一樣硬件平臺佔用的大小和對齊值均可能是不同的。
【在 Go 中恰到好處的內存對齊】https://eddycjy.gitbook.io/golang/di-1-ke-za-tan/go-memory-align
【golang 內存對齊】https://xie.infoq.cn/article/594a7f54c639accb53796cfc7
【C/C++內存對齊詳解】https://zhuanlan.zhihu.com/p/30007037
【內存對齊詳解】https://developer.aliyun.com/article/32177
【go內存對齊】https://www.kancloud.cn/golang_programe/golang/1144263
【Go struct 內存對齊】https://geektutu.com/post/hpg-struct-alignment.html
【Memory access granularity】https://developer.ibm.com/articles/pa-dalign/?mhsrc=ibmsearch_a&mhq=Straighten up and fly right
【爲何要內存對齊 Data alignment: Straighten up and fly right】https://blog.csdn.net/lgouc/article/details/8235471