版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第詳解Go語言中的內(nèi)存對(duì)齊目錄前言基礎(chǔ)知識(shí)看個(gè)問題什么是內(nèi)存對(duì)齊為什么需要內(nèi)存對(duì)齊unsafe.AlignOf()內(nèi)存對(duì)齊規(guī)則舉個(gè)例子空結(jié)構(gòu)體的對(duì)齊規(guī)則總結(jié)
前言
前面有篇文章我們學(xué)習(xí)了Go語言空結(jié)構(gòu)體詳解,最近又在看unsafe包的知識(shí),在查閱相關(guān)資料時(shí)不免會(huì)看到內(nèi)存對(duì)齊相關(guān)的內(nèi)容,雖然感覺這類知識(shí)比較底層,但是看到了卻不深究和渣男有什么區(qū)別?雖然我不會(huì),但我可以學(xué),那么這篇文章,我們就一起來看下什么是內(nèi)存對(duì)齊吧!
說明:本文中的測(cè)試示例,均是基于Go1.1764位機(jī)器
基礎(chǔ)知識(shí)
在Go語言中,我們可以通過unsafe.Sizeof(x)來確定一個(gè)變量占用的內(nèi)存字節(jié)數(shù)(不包含x所指向的內(nèi)容的大?。?/p>
例如對(duì)于字符串?dāng)?shù)組,在64位機(jī)器上,unsafe.Sizeof()返回的任意字符串?dāng)?shù)組大小為24字節(jié),和其底層數(shù)據(jù)無關(guān):
func
main()
{
s
:=
[]string{"1",
"2",
"3"}
s2
:=
[]string{"1"}
fmt.Println(unsafe.Sizeof(s))
//
24
fmt.Println(unsafe.Sizeof(s2))
//
24
對(duì)于Go語言的內(nèi)置類型,占用內(nèi)存大小如下:
類型字節(jié)數(shù)bool1個(gè)字節(jié)intN,uintN,floatN,complexNN/8個(gè)字節(jié)(int32是4個(gè)字節(jié))int,uint,uintptr計(jì)算機(jī)字長(zhǎng)/8(64位是8個(gè)字節(jié))*T,map,func,chan計(jì)算機(jī)字長(zhǎng)/8(64位是8個(gè)字節(jié))string(data、len)2*計(jì)算機(jī)字長(zhǎng)/8(64位是16個(gè)字節(jié))interface(tab、data或_type、data)2*計(jì)算機(jī)字長(zhǎng)/8(64位是16個(gè)字節(jié))[]T(array、len、cap)3*計(jì)算機(jī)字長(zhǎng)/8(64位是24個(gè)字節(jié))
func
main()
{
fmt.Println(unsafe.Sizeof(int(1)))
//
8
fmt.Println(unsafe.Sizeof(uintptr(1)))
//
8
fmt.Println(unsafe.Sizeof(map[string]string{}))
//
8
fmt.Println(unsafe.Sizeof(string("")))
//
16
fmt.Println(unsafe.Sizeof([]string{}))
//
24
var
a
interface{}
fmt.Println(unsafe.Sizeof(a))
//
16
看個(gè)問題
基于上面的理解,那么對(duì)于一個(gè)結(jié)構(gòu)體來說,占用內(nèi)存大小就應(yīng)該等于多個(gè)基礎(chǔ)類型占用內(nèi)存大小的和,我們就結(jié)合幾個(gè)示例來看下:
type
Example
struct
{
a
bool
//
1個(gè)字節(jié)
b
int
//
8個(gè)字節(jié)
c
string
//
16個(gè)字節(jié)
func
main()
{
fmt.Println(unsafe.Sizeof(Example{}))
//
32
Example結(jié)構(gòu)體的三個(gè)基礎(chǔ)類型,加起來一個(gè)25字節(jié),但是最終輸出的卻是32字節(jié)。
我們?cè)倏磧蓚€(gè)結(jié)構(gòu)體,即使這兩個(gè)結(jié)構(gòu)體包含的字段類型一致,但是順序不一致,最終輸出的大小也不一樣:
type
A
struct
{
a
int32
b
int64
c
int32
type
B
struct
{
a
int32
b
int32
c
int64
func
main()
{
fmt.Println(unsafe.Sizeof(A{}))
//
24
fmt.Println(unsafe.Sizeof(B{}))
//
16
是什么導(dǎo)致了上述問題的呢,這就引出了我們要看的知識(shí)點(diǎn):內(nèi)存對(duì)齊。
什么是內(nèi)存對(duì)齊
我們知道,在計(jì)算機(jī)中訪問一個(gè)變量,需要訪問它的內(nèi)存地址,從理論上講似乎對(duì)任何類型的變量的訪問可以從任何地址開始,但實(shí)際情況是:在訪問特定類型變量的時(shí)候通常在特定的內(nèi)存地址訪問,這就需要對(duì)這些數(shù)據(jù)在內(nèi)存中存放的位置有限制,各種類型數(shù)據(jù)按照一定的規(guī)則在空間上排列,而不是順序的一個(gè)接一個(gè)的排放,這就是對(duì)齊。
內(nèi)存對(duì)齊是編譯器的管轄范圍。表現(xiàn)為:編譯器為程序中的每個(gè)數(shù)據(jù)單元安排在適當(dāng)?shù)奈恢蒙稀?/p>
為什么需要內(nèi)存對(duì)齊
有些CPU可以訪問任意地址上的任意數(shù)據(jù),而有些CPU只能在特定地址訪問數(shù)據(jù),因此不同硬件平臺(tái)具有差異性,這樣的代碼就不具有移植性,如果在編譯時(shí),將分配的內(nèi)存進(jìn)行對(duì)齊,這就具有平臺(tái)可以移植性了。CPU訪問內(nèi)存時(shí)并不是逐個(gè)字節(jié)訪問,而是以字長(zhǎng)(wordsize)為單位訪問,例如32位的CPU字長(zhǎng)是4字節(jié),64位的是8字節(jié)。如果變量的地址沒有對(duì)齊,可能需要多次訪問才能完整讀取到變量?jī)?nèi)容,而對(duì)齊后可能就只需要一次內(nèi)存訪問,因此內(nèi)存對(duì)齊可以減少CPU訪問內(nèi)存的次數(shù),加大CPU訪問內(nèi)存的吞吐量。
假設(shè)每次訪問的步長(zhǎng)為4個(gè)字節(jié),如果未經(jīng)過內(nèi)存對(duì)齊,獲取b的數(shù)據(jù)需要進(jìn)行兩次內(nèi)存訪問,最后再進(jìn)行數(shù)據(jù)整理得到b的完整數(shù)據(jù):
image-20250313230839425
如果經(jīng)過內(nèi)存對(duì)齊,一次內(nèi)存訪問就能得到b的完整數(shù)據(jù),減少了一次內(nèi)存訪問:
image-20250313231143302
unsafe.AlignOf()
unsafe.AlignOf(x)方法的返回值是m,當(dāng)變量進(jìn)行內(nèi)存對(duì)齊時(shí),需要保證分配到x的內(nèi)存地址能夠整除m。因此可以通過這個(gè)方法,確定變量x在內(nèi)存對(duì)齊時(shí)的地址:
對(duì)于任意類型的變量x,unsafe.Alignof(x)至少為1。對(duì)于struct結(jié)構(gòu)體類型的變量x,計(jì)算x每一個(gè)字段f的unsafe.Alignof(x.f),unsafe.Alignof(x)等于其中的最大值。對(duì)于array數(shù)組類型的變量x,unsafe.Alignof(x)等于構(gòu)成數(shù)組的元素類型的對(duì)齊倍數(shù)。
對(duì)于系統(tǒng)內(nèi)置基礎(chǔ)類型變量x,unsafe.Alignof(x)的返回值就是min(字長(zhǎng)/8,unsafe.Sizeof(x)),即計(jì)算機(jī)字長(zhǎng)與類型占用內(nèi)存的較小值:
func
main()
{
fmt.Println(unsafe.Alignof(int(1)))
//
1
--
min(8,1)
fmt.Println(unsafe.Alignof(int32(1)))
//
4
--
min
(8,4)
fmt.Println(unsafe.Alignof(int64(1)))
//
8
--
min
(8,8)
fmt.Println(unsafe.Alignof(complex128(1)))
//
8
--
min(8,16)
內(nèi)存對(duì)齊規(guī)則
我們講內(nèi)存對(duì)齊,就是把變量放在特定的地址,那么如何計(jì)算特定地址呢,這就涉及到內(nèi)存對(duì)齊規(guī)則:
成員對(duì)齊規(guī)則
針對(duì)一個(gè)基礎(chǔ)類型變量,如果unsafe.AlignOf()返回的值是m,那么該變量的地址需要被m整除(如果當(dāng)前地址不能整除,填充空白字節(jié),直至可以整除)。
整體對(duì)齊規(guī)則
針對(duì)一個(gè)結(jié)構(gòu)體,如果unsafe.AlignOf()返回值是m,需要保證該結(jié)構(gòu)體整體內(nèi)存占用是m的整數(shù)倍,如果當(dāng)前不是整數(shù)倍,需要在后面填充空白字節(jié)。
通過內(nèi)存對(duì)齊后,就可以保證在訪問一個(gè)變量地址時(shí):
如果該變量占用內(nèi)存小于字長(zhǎng):保證一次訪問就能得到數(shù)據(jù);如果該變量占用內(nèi)存大于字長(zhǎng):保證第一次內(nèi)存訪問的首地址,是該變量的首地址。
舉個(gè)例子
例1:
type
A
struct
{
a
int32
b
int64
c
int32
func
main()
{
fmt.Println(unsafe.Sizeof(A{1,
1,
1}))
//
24
1.第一個(gè)字段是int32類型,unsafe.Sizeof(int32(1))=4,內(nèi)存占用為4個(gè)字節(jié),同時(shí)unsafe.Alignof(int32(1))=4,內(nèi)存對(duì)齊需保證變量首地址可以被4整除,我們假設(shè)地址從0開始,0可以被4整除:
成員變量1內(nèi)存對(duì)齊
2.第二個(gè)字段是int64類型,unsafe.Sizeof(int64(1))=8,內(nèi)存占用為8個(gè)字節(jié),同時(shí)unsafe.Alignof(int64(1))=8,需保證變量放置首地址可以被8整除,當(dāng)前地址為4,距離4最近的且可以被8整除的地址為8,因此需要添加四個(gè)空白字節(jié),從8開始放置:
成員變量2內(nèi)存對(duì)齊
3.第三個(gè)字段是int32類型,unsafe.Sizeof(int32(1))=4,內(nèi)存占用為4個(gè)字節(jié),同時(shí)unsafe.Alignof(int32(1))=4,內(nèi)存對(duì)齊需保證變量首地址可以被4整除,當(dāng)前地址為16,16可以被4整除:
成員變量3內(nèi)存對(duì)齊
4.所有成員對(duì)齊都已經(jīng)完成,現(xiàn)在我們需要看一下整體對(duì)齊規(guī)則:unsafe.Alignof(A{})=8,即三個(gè)變量成員的最大值,內(nèi)存對(duì)齊需要保證該結(jié)構(gòu)體的內(nèi)存占用是8的整數(shù)倍,當(dāng)前內(nèi)存占用是20個(gè)字節(jié),因此需要再補(bǔ)充4個(gè)字節(jié):
整體對(duì)齊
5.最終該結(jié)構(gòu)體的內(nèi)存占用為24字節(jié)。
例二:
type
B
struct
{
a
int32
b
int32
c
int64
func
main()
{
fmt.Println(unsafe.Sizeof(B{1,
1,
1}))
//
16
1.第一個(gè)字段是int32類型,unsafe.Sizeof(int32(1))=4,內(nèi)存占用為4個(gè)字節(jié),同時(shí)unsafe.Alignof(int32(1))=4,內(nèi)存對(duì)齊需保證變量首地址可以被4整除,我們假設(shè)地址從0開始,0可以被4整除:
成員變量1內(nèi)存對(duì)齊
2.第二個(gè)字段是int32類型,unsafe.Sizeof(int32(1))=4,內(nèi)存占用為4個(gè)字節(jié),同時(shí)unsafe.Alignof(int32(1))=4,內(nèi)存對(duì)齊需保證變量首地址可以被4整除,當(dāng)前地址為4,4可以被4整除:
成員變量2內(nèi)存對(duì)齊
3.第三個(gè)字段是int64類型,unsafe.Sizeof(int64(1))=8,內(nèi)存占用為8個(gè)字節(jié),同時(shí)unsafe.Alignof(int64(1))=8,內(nèi)存對(duì)齊需保證變量首地址可以被8整除,當(dāng)前地址為8,8可以被8整除:
成員變量3內(nèi)存對(duì)齊
4.所有成員對(duì)齊都已經(jīng)完成,現(xiàn)在我們需要看一下整體對(duì)齊規(guī)則:unsafe.Alignof(B{})=8,即三個(gè)變量成員的最大值,內(nèi)存對(duì)齊需要保證該結(jié)構(gòu)體的內(nèi)存占用是8的整數(shù)倍,當(dāng)前內(nèi)存占用是16個(gè)字節(jié),已經(jīng)符合規(guī)則,最終該結(jié)構(gòu)體的內(nèi)存占用為16個(gè)字節(jié)。
空結(jié)構(gòu)體的對(duì)齊規(guī)則
如果空結(jié)構(gòu)體作為結(jié)構(gòu)體的內(nèi)置字段:當(dāng)變量位于結(jié)構(gòu)體的前面和中間時(shí),不會(huì)占用內(nèi)存;當(dāng)該變量位于結(jié)構(gòu)體的末尾位置時(shí),需要進(jìn)行內(nèi)存對(duì)齊,內(nèi)存占用大小和前一個(gè)變量的大小保持一致。
type
C
struct
{
a
struct{}
b
int64
c
int64
type
D
struct
{
a
int64
b
struct{}
c
int64
type
E
struct
{
a
int64
b
int64
c
struct{}
type
F
struct
{
a
int32
b
int32
c
struct{}
func
main()
{
fmt.Println(unsafe.Sizeof(C{}))
//
16
fmt.Println(unsafe.Sizeof(D{}))
//
16
fmt.Println(unsafe.Sizeof(E{}))
//
24
fmt.Println(unsafe.Sizeof(F{}))
//
12
總結(jié)
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024-2025學(xué)年山東省菏澤市高二下學(xué)期期中考試歷史試題(A)(解析版)
- 2024-2025學(xué)年江蘇省鹽城市高二下學(xué)期期終考試歷史試題(解析版)
- 2026年生物與醫(yī)學(xué)前沿科技知識(shí)競(jìng)賽題集
- 2026年計(jì)算機(jī)應(yīng)用基礎(chǔ)初級(jí)水平測(cè)試題
- 2026年心理學(xué)入門認(rèn)知心理學(xué)與社會(huì)心理學(xué)試題庫
- 2026年城市規(guī)劃領(lǐng)域?qū)I(yè)技術(shù)人員考試練習(xí)題集
- 2026年文化常識(shí)與歷史知識(shí)綜合測(cè)試題
- 2026年高考化學(xué)模擬試題及答案解析
- 2026年寫作技巧基礎(chǔ)訓(xùn)練初級(jí)自測(cè)模擬題
- 2026年房地產(chǎn)銷售經(jīng)理人才選拔模擬測(cè)試
- 頸內(nèi)靜脈血栓的護(hù)理
- 操作工技能等級(jí)評(píng)級(jí)方案
- 國(guó)家職業(yè)技術(shù)技能標(biāo)準(zhǔn) 5-05-02-01 農(nóng)作物植保員 人社廳發(fā)202021號(hào)
- 購房委托書范文
- 素描第2版(藝術(shù)設(shè)計(jì)相關(guān)專業(yè))全套教學(xué)課件
- 新生兒先天性腎上腺皮質(zhì)增生癥
- 中國(guó)傳統(tǒng)木雕工藝美術(shù)的繼承與發(fā)展-以平遙木雕神像傳統(tǒng)技藝為例
- (完整版)四宮格數(shù)獨(dú)題目204道(可直接打印)及空表(一年級(jí)數(shù)獨(dú)題練習(xí))
- DB32/T+4539-2023+淡水生物環(huán)境DNA監(jiān)測(cè)技術(shù)方法
- 火電廠鍋爐運(yùn)行與維護(hù)
- CTM系列產(chǎn)品使用手冊(cè)
評(píng)論
0/150
提交評(píng)論