Golang內(nèi)存對齊的規(guī)則及實現(xiàn)
什么是內(nèi)存對齊?
編譯器會將數(shù)據(jù)按照特定的規(guī)則,把數(shù)據(jù)安排到合適的存儲地址上,并占用合適的地址長度
為什么要內(nèi)存對齊
保證程序順利高效的運行,可以讓CPU快速從內(nèi)存中存取到字段,避免資源浪費
內(nèi)存對齊規(guī)則
1、起始的存儲地址 必須是 內(nèi)存對齊邊界 的倍數(shù)。
2、整體占用字節(jié)數(shù) 必須是 內(nèi)存對齊邊界 的倍數(shù)。
Tip:先聲明兩個概念 ↓ ↓
- 內(nèi)存對齊邊界:結(jié)構(gòu)體所有元素中,哪個元素占用的字節(jié)數(shù)大,那么這個元素占用的字節(jié)數(shù)就是內(nèi)存對齊邊界
- 對齊邊界:結(jié)構(gòu)體中每個元素自己占用的字節(jié)數(shù)
通過下邊的示例來理解內(nèi)存對齊的規(guī)則
首先我們定義一個結(jié)構(gòu)體:
type S struct { A uint8 // byte:1 B int32 // byte:4 C int16 // byte:2 D int64 // byte:8 E [2]string // byte總和:32 -->string類型數(shù)組,總共2個元素,每個元素包含2部分內(nèi)容: // 內(nèi)容1:ptr,指向存放數(shù)據(jù)的地址,byte:8: 內(nèi)容2:len,標識字符串長度的整數(shù)值,byte:8 F struct{} // zero size field }
步驟一:確定內(nèi)存對齊邊界
統(tǒng)計出結(jié)構(gòu)體中所有的元素分別占用的字節(jié)數(shù),占用最大的字節(jié)數(shù)就是內(nèi)存對齊邊界,在這個示例中的內(nèi)存對齊邊界就是 8 Tip:如果不知道怎樣確定內(nèi)存對齊邊界,可以使用unsafe.Alignof()函數(shù)打印每個元素的對齊系數(shù),打印中的最大值就是內(nèi)存對齊邊界:
func main() { fmt.Println(unsafe.Alignof(S{}.A)) // output: 1 fmt.Println(unsafe.Alignof(S{}.B)) // output: 4 fmt.Println(unsafe.Alignof(S{}.C)) // output: 2 fmt.Println(unsafe.Alignof(S{}.D)) // output: 8 fmt.Println(unsafe.Alignof(S{}.E)) // output: 8 fmt.Println(unsafe.Alignof(S{}.F)) // output: 1 fmt.Println(unsafe.Sizeof(S{})) //可以直接打印出結(jié)構(gòu)體所占用的字節(jié)數(shù) }
步驟二:確定起始存儲地址
內(nèi)存對齊規(guī)則第一條:起始的存儲地址 必須是 內(nèi)存對齊邊界 的倍數(shù)。也就是(起始地址addr)%(內(nèi)存對齊邊界)=0,在我們示例中起始地址就是addr%8=0,我們從0地址開始,為了方便看,我們先用圖來展示地址存儲分布圖
起始地址為0,0%8=0,符合條件,那我們就從0開始存儲數(shù)據(jù)
其中元素A占用1個字節(jié),并且0%1=0,符合條件,第0個地址分配給A;
元素B占用4個字節(jié),從1個地址分配的話,1%4≠0,只有4%4=0,所以把第4-7個地址分配給B;
以此類推······ 具體分配如下
元素A:1個字節(jié),占用第0個相對地址空間, 0%1 =0(起始地址為0,內(nèi)存邊界為1)
元素B:4個字節(jié),占用第4-7個相對地址空間, 4%4 =0(起始地址為4,內(nèi)存邊界為4)
元素C:2個字節(jié),占用第8-9個相對地址空間, 8%2 =0(起始地址為8,內(nèi)存邊界為2)
元素D:8個字節(jié),占用第16-23個相對地址空間, 16%8=0(起始地址為16,內(nèi)存邊界為8)
元素E:8*2*2個字節(jié),占用第24-31和32-39和40-47和48-55個相對地址空間,24%8=0...(起始地址為24,內(nèi)存邊界為8...)
元素F:zero size field,占用第56個相對地址空間, 56%1=0 (起始地址為56,內(nèi)存邊界為1)
最終內(nèi)存在第56個相對地址空間分配完畢,但是我們的地址空間是從0開始計算的,所以目前來看結(jié)構(gòu)體總共占用57個字節(jié)!
步驟三:確定結(jié)構(gòu)體占用字節(jié)數(shù)
我們在步驟二中排列出了結(jié)構(gòu)體中元素的存放地址,整體元素占用57個字節(jié),但是到這里還不算完事兒,因為我們還沒有執(zhí)行第二條的內(nèi)存對齊規(guī)則–>整體占用字節(jié)數(shù) 必須是 內(nèi)存對齊邊界 的倍數(shù)。我們的內(nèi)存對齊邊界為8,而57不是8的倍數(shù),所以我們需要擴張字節(jié)空間到8的倍數(shù),延伸到64,也就是擴張到到圖中的第63個相對地址空間。這個結(jié)構(gòu)體占用的字節(jié)數(shù)為64字節(jié)!
內(nèi)存空間優(yōu)化
通過上邊的示例與圖表我們不難看出,其中還有好多個地址空間被浪費掉了,這些沒被利用的地址空間,go語言會進行padding操作來對這些空間進行填充,使這些空間變成合法的內(nèi)存空間。
我們再思考一下,如何才能減少地址空間的浪費呢?能不能通過重新排列元素的位置來合理的分配地址空間呢?答案當然是肯定的,我們可以通過合理排列元素的定義順序來減少地址空間的浪費。
我們先看結(jié)論,下邊是重新排列后的結(jié)構(gòu)體:
type S1 struct { A uint8 F struct{} C int16 B int32 D int64 E [2]string }
再看一下重新分配地址空間的圖標:
起始地址為0,0%8=0,符合條件,我們就從0開始存儲數(shù)據(jù)
其中元素A占用1個字節(jié),并且0%1=0,符合條件,第0個地址分配給A; 元素F占用1個字節(jié),1%1=0,符合條件,將第1個地址分配給B; 以此類推······ 具體分配如下
元素A:1個字節(jié),占用第0個相對地址空間, 0%1 =0(起始地址為0,內(nèi)存邊界為1)
元素F:zero size field,占用第1個相對地址空間,1%1 =0(起始地址為1,內(nèi)存邊界為1)
元素C:2個字節(jié),占用第2-3個相對地址空間, 2%2 =0(起始地址為2,內(nèi)存邊界為2)
元素B:4個字節(jié),占用第4-7個相對地址空間, 4%4=0(起始地址為4,內(nèi)存邊界為4)
元素D:8個字節(jié),占用第8-15個相對地址空間, 8%8=0(起始地址為8,內(nèi)存邊界為8)
元素E:8*2*2個字節(jié),占用第16-23和24-31和32-39和40-47個相對地址空間,16%8=0... (起始地址為16,內(nèi)存邊界為8...)
最終內(nèi)存在第47個相對地址空間分配完畢,但是我們的地址空間是從0開始計算的,所以目前來看結(jié)構(gòu)體總共占用48個字節(jié)!并且48還是內(nèi)存對齊邊界值8的整數(shù)倍,所以結(jié)構(gòu)體最終占用48個字節(jié)!
我們可以很明顯的看出來,在我們改變元素的定義順序后,占用的字節(jié)空間從64字節(jié)減少到了48字節(jié),內(nèi)存空間得到了充分的優(yōu)化?。?!這也是一個結(jié)論所在,我們在結(jié)構(gòu)體定義變量的時候,盡量將相同類型的變量定義在一起,將占用字節(jié)較少的變量類型放在一塊。
到此這篇關(guān)于Golang內(nèi)存對齊的規(guī)則及實現(xiàn)的文章就介紹到這了,更多相關(guān)Golang內(nèi)存對齊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言異常處理(Panic和recovering)用法詳解
異常處理是程序健壯性的關(guān)鍵,往往開發(fā)人員的開發(fā)經(jīng)驗的多少從異常部分處理上就能得到體現(xiàn)。Go語言中沒有Try?Catch?Exception機制,但是提供了panic-and-recover機制,本文就來詳細講講他們的用法2022-07-07Goland使用Go Modules創(chuàng)建/管理項目的操作
這篇文章主要介紹了Goland使用Go Modules創(chuàng)建/管理項目的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-05-05golang中import cycle not allowed解決的一種思路
這篇文章主要給大家介紹了關(guān)于golang中import cycle not allowed解決的一種思路,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08go語言實現(xiàn)將重要數(shù)據(jù)寫入圖片中
本文給大家分享的是go語言實現(xiàn)將數(shù)據(jù)的二進制形式寫入圖像紅色通道數(shù)據(jù)二進制的低位,從而實現(xiàn)將重要數(shù)據(jù)隱藏,有需要的小伙伴參考下吧。2015-03-03Windows上安裝Go并配置環(huán)境變量(圖文步驟)
開始使用Go創(chuàng)建應(yīng)用程序之前,需要設(shè)置開發(fā)環(huán)境,本文主要介紹了Windows上安裝Go并配置環(huán)境變量,具有一定的參考價值,感興趣的可以了解一下2023-08-08