Golang 記憶體對齊問題
什麼是記憶體對齊?
CPU把記憶體當成是一塊一塊的,塊的大小可以是2,4,8,16位元組大小,因此CPU在讀取記憶體時是一塊一塊進行讀取的。塊大小成為memory access granularity(粒度)。

sda.png
假設CPU訪問粒度是4,也就是一次性可以讀取記憶體中的四個位元組內容;當我們不採用記憶體對齊策略,如果需要訪問A中的b元素,CPU需要先取出0-3四個位元組的內容,發現沒有讀取完,還需要再次讀取,一共需要進行兩次訪問記憶體的操作;而有了記憶體對齊,參考左圖,可一次性取出4-7四個位元組的元素也即是b,這樣就只需要進行一次訪問記憶體的操作。所以作業系統這樣做的原因也就是所謂的拿空間換時間,提高效率。
為什麼要記憶體對齊?
會了關於結構體記憶體大小的計算,可是為什麼系統要對於結構體資料進行記憶體對齊呢,很明顯所佔用的空間大小要更多。原因可歸納如下:
- 平臺原因(移植原因):不是所有的硬體平臺都能訪問任意地址上的任意資料的;某些硬體平臺只能在某些地址處取某些特定型別的資料,否則丟擲硬體異常。
- 效能原因:資料結構(尤其是棧)應該儘可能地在自然邊界上對齊。原因在於,為了訪問未對齊的記憶體,處理器需要作兩次記憶體訪問;而對齊的記憶體訪問僅需要一次訪問。
Golang 位元組對齊
最近在做一個需求的時候,有個場景,需要一個執行緒定時去更新一個全域性變數指標地址,然後在另外的執行緒可以讀取這個變數的資料,同事在幫忙Review程式碼的時候,問這個多執行緒操作這個全域性指標變數時候需不需要加鎖,因為在C/C++中有記憶體對齊問題,如果指標是記憶體對齊的,是可以不加鎖的。所以下面測試下golang的記憶體是否會做自動對齊的操作。
測試一
//輸出長度為1 fmt.Printf("%d",unsafe.Sizeof(struct { i8int8 }{}))
測試二
//輸出長度為16 fmt.Printf("%d",unsafe.Sizeof(struct { i8int8 p*int8 }{}))
在測試二中可以看出, 在後面申明一個指標以後,記憶體空間自動擴容為16了,說明編譯自動幫我們做了記憶體對齊。
參考文獻
ofollow,noindex">http://www.cppblog.com/snailcong/archive/2009/03/16/76705.html
https://www.zhihu.com/question/27862634
https://blog.csdn.net/sssssuuuuu666/article/details/75175108