GO 記憶體對齊

2021-10-10 17:02:39 字數 3207 閱讀 7627

之前遇到過這樣乙個情況(發現問題的結構體並不長這樣, 不過為了引出問題, 改了一下):

type test structfunc main()fmt.printf("%d", unsafe.sizeof(t))

}

建立乙個結構體, 檢視一下其記憶體占用. 看結果前先簡單算一下:

這麼算下來的話,test結構體占用應該是:1+4+1+8+1=15b. 15個位元組對吧. 來, 列印看一下:

32個位元組???這不坑我麼.記憶體占用直接多出一倍.

通過查詢資料, 發現了這樣乙個名詞:記憶體對齊. 什麼是記憶體對齊呢?

簡單說, 就是cpu在讀取資料的時候, 並不是乙個位元組乙個位元組讀取的, 而是一塊一塊讀取的. 那麼這個快是多大呢? 根據cpu位數不同而不同.

go編譯器在編譯的時候, 為了保證記憶體對齊, 對每乙個資料型別都給出了對齊保證, 將未對齊的記憶體留空. 如果乙個型別的對齊保證是4b, 那麼其資料存放的起始位址偏移量必是4b 的整數倍. 而編譯器給出的這個對齊保證是多少呢? 不同版本不同平台的編譯器不盡相同, 可以通過函式unsafe.alignof來獲取.

通過分析之前的資料結果, 就能大致理解了. 先來看一下幾個型別對齊保證的值:

fmt.printf("bool: %d\n", unsafe.alignof(bool(false)))fmt.printf("int32: %d\n", unsafe.alignof(int32(0)))fmt.printf("int8: %d\n", unsafe.alignof(int8(0)))

fmt.printf("int64: %d\n", unsafe.alignof(int64(0)))

fmt.printf("byte: %d\n", unsafe.alignof(byte(0)))

結果如下:

來嘗試乙個乙個放到記憶體中(下圖中每個空白代表乙個位元組):

1.放入bool: 其對齊保證為1, 第乙個變數, 直接放入即可.

2.放入int32. 其對齊保證為4, 既偏移量為4的整數倍. 而現有位址中, 首個4的整數倍為第四個位元組(中間三位元組留空).

按照這個思路, 依次將後面的變數放入, 結果占用的記憶體為(其中字母依次為變數占用,x為對齊留空):

a*** bbbb c*** ***x dddd dddd e

但是這才25個位元組啊. 和實際的32位元組還差點呢. 別急, 再看一下結構體的對齊保證, 發現是8b. 上面不是8b 的整數倍, 往後補零. 結果:

a*** bbbb c*** ***x dddd dddd e*** ***x

如此一來, 就正好32位了. 結構體的對齊保證, 為其成員變數對齊保證的最大值.

那麼編譯器為什麼要做記憶體對齊這種事情呢? 舉個例子, 如果不做記憶體對齊, 那麼下面這個結構體的記憶體分布為:

type test structabbb b還記得之前說,cpu讀取記憶體是一塊一塊讀取的麼? 而這個塊, 假設是4b.

這樣的話, 當你需要讀取i3變數的時候, 需要進行兩次記憶體訪問. 而對齊之後, 只需要進行一次記憶體訪問即可. 是典型的空間換時間的做法.

既然知道了問題出在**, 那麼是不是如果換一下欄位的存放順序, 就可以壓縮記憶體空間了呢? 思路很簡單, 將對齊保證小的放到前面, 試一下:

通過之前的對齊分析. 結果確為18b. 也就是因為字段順序的問題, 編譯器為了保證記憶體對齊, 向其中填充了很多空白, 造成了記憶體的浪費.

僅僅是修改了一下欄位的順序, 就可以將結構體的記憶體占用直接降低一倍. 見識了...

那麼, 有沒有什麼辦法能夠幫我們檢測是否存在記憶體對齊的優化呢? 畢竟平常寫的時候, 誰會關心這玩意呢. 別說, 還真有.golangci-lint

官網: 

安裝:brew install golangci-lint

檢測所有檔案命令:golangci-lint run ./..

檢測一下最開始的結構體檔案(新增引數指定檢測記憶體對齊):

golangci-lint run --disable-all -e maligned main.go

看到結果:

會看到提示, 該結構體當前占有32b, 可優化至16b. 完美.

當然, 此工具的功能不僅如此, 它能夠提供很多建議, 有待發掘.

其實, 專案中估計也很少有關注記憶體對齊的時候吧. 不過畢竟積少成多, 記憶體這玩意, 能省則省嘛.

Go記憶體對齊

fmt.println unsafe.sizeof int64 0 8 type sizeofa struct unsafe.sizeof sizeofa 8 type sizeofc struct unsafe.sizeof sizeofc 8 unsafe.alignof sizeofc 4 結...

GO語言 記憶體對齊

32位系統,一次可以取32位資料,也就是4位元組,64位是8位元組,即32為作業系統中記憶體是4位元組對齊,而對於64為作業系統是8位元組對齊 記憶體對齊的目的是為了能夠快速的訪問記憶體進行資料訪問,但是會損耗記憶體,即空間換時間 首先 package main import fmt reflect...

記憶體對齊 記憶體對齊規則解釋 記憶體對齊原理

一 記憶體對齊的原因 我們都知道計算機是以位元組 byte 為單位劃分的,理論上來說cpu是可以訪問任一編號的位元組資料的,我們又知道cpu的定址其實是通過位址匯流排來訪問記憶體的,cpu又分為32位和64位,在32位的cpu一次可以處理4個位元組 byte 的資料,那麼cpu實際定址的步長就是4個...