最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Go語言中的內(nèi)存對齊

 更新時(shí)間:2022年10月09日 16:49:46   作者:漫漫Coding路  
前面我們學(xué)習(xí)了Go語言空結(jié)構(gòu)體詳解,最近又在看unsafe包的知識,在查閱相關(guān)資料時(shí)不免會(huì)看到內(nèi)存對齊相關(guān)的內(nèi)容。雖然不會(huì),但可以學(xué)呀,那么這篇文章,我們就一起來看下什么是內(nèi)存對齊吧

前言

前面有篇文章我們學(xué)習(xí)了 Go 語言空結(jié)構(gòu)體詳解,最近又在看 unsafe包的知識,在查閱相關(guān)資料時(shí)不免會(huì)看到內(nèi)存對齊相關(guān)的內(nèi)容,雖然感覺這類知識比較底層,但是看到了卻不深究和渣男有什么區(qū)別?雖然我不會(huì),但我可以學(xué),那么這篇文章,我們就一起來看下什么是內(nèi)存對齊吧!

說明:本文中的測試示例,均是基于Go1.17 64位機(jī)器

基礎(chǔ)知識

在Go語言中,我們可以通過 unsafe.Sizeof(x) 來確定一個(gè)變量占用的內(nèi)存字節(jié)數(shù)(不包含 x 所指向的內(nèi)容的大小)。

例如對于字符串?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
}

對于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ī)字長/8  (64位 是 8 個(gè)字節(jié))
*T, map, func, chan計(jì)算機(jī)字長/8  (64位 是 8 個(gè)字節(jié))
string (data、len)2 * 計(jì)算機(jī)字長/8 (64位 是 16 個(gè)字節(jié))
interface (tab、data 或 _type、data)2 * 計(jì)算機(jī)字長/8 (64位 是 16 個(gè)字節(jié))
[]T (array、len、cap)3 * 計(jì)算機(jī)字長/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è)問題

基于上面的理解,那么對于一個(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é)

我們再看兩個(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)致了上述問題的呢,這就引出了我們要看的知識點(diǎn):內(nèi)存對齊。

什么是內(nèi)存對齊

我們知道,在計(jì)算機(jī)中訪問一個(gè)變量,需要訪問它的內(nèi)存地址,從理論上講似乎對任何類型的變量的訪問可以從任何地址開始,但實(shí)際情況是:在訪問特定類型變量的時(shí)候通常在特定的內(nèi)存地址訪問,這就需要對這些數(shù)據(jù)在內(nèi)存中存放的位置有限制,各種類型數(shù)據(jù)按照一定的規(guī)則在空間上排列,而不是順序的一個(gè)接一個(gè)的排放,這就是對齊。

內(nèi)存對齊是編譯器的管轄范圍。表現(xiàn)為:編譯器為程序中的每個(gè)“數(shù)據(jù)單元”安排在適當(dāng)?shù)奈恢蒙稀?/p>

為什么需要內(nèi)存對齊

  • 有些CPU可以訪問任意地址上的任意數(shù)據(jù),而有些CPU只能在特定地址訪問數(shù)據(jù),因此不同硬件平臺具有差異性,這樣的代碼就不具有移植性,如果在編譯時(shí),將分配的內(nèi)存進(jìn)行對齊,這就具有平臺可以移植性了。
  • CPU 訪問內(nèi)存時(shí)并不是逐個(gè)字節(jié)訪問,而是以字長(word size)為單位訪問,例如 32位的CPU 字長是4字節(jié),64位的是8字節(jié)。如果變量的地址沒有對齊,可能需要多次訪問才能完整讀取到變量內(nèi)容,而對齊后可能就只需要一次內(nèi)存訪問,因此內(nèi)存對齊可以減少CPU訪問內(nèi)存的次數(shù),加大CPU訪問內(nèi)存的吞吐量。

假設(shè)每次訪問的步長為4個(gè)字節(jié),如果未經(jīng)過內(nèi)存對齊,獲取b的數(shù)據(jù)需要進(jìn)行兩次內(nèi)存訪問,最后再進(jìn)行數(shù)據(jù)整理得到b的完整數(shù)據(jù):

image-20220313230839425

如果經(jīng)過內(nèi)存對齊,一次內(nèi)存訪問就能得到b的完整數(shù)據(jù),減少了一次內(nèi)存訪問:

image-20220313231143302

unsafe.AlignOf()

unsafe.AlignOf(x) 方法的返回值是 m,當(dāng)變量進(jìn)行內(nèi)存對齊時(shí),需要保證分配到 x 的內(nèi)存地址能夠整除 m。因此可以通過這個(gè)方法,確定變量x 在內(nèi)存對齊時(shí)的地址:

  • 對于任意類型的變量 x ,unsafe.Alignof(x) 至少為 1。
  • 對于 struct 結(jié)構(gòu)體類型的變量 x,計(jì)算 x 每一個(gè)字段 f 的 unsafe.Alignof(x.f)unsafe.Alignof(x) 等于其中的最大值。
  • 對于 array 數(shù)組類型的變量 x,unsafe.Alignof(x) 等于構(gòu)成數(shù)組的元素類型的對齊倍數(shù)。

對于系統(tǒng)內(nèi)置基礎(chǔ)類型變量 x ,unsafe.Alignof(x) 的返回值就是 min(字長/8,unsafe.Sizeof(x)),即計(jì)算機(jī)字長與類型占用內(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)存對齊規(guī)則

我們講內(nèi)存對齊,就是把變量放在特定的地址,那么如何計(jì)算特定地址呢,這就涉及到內(nèi)存對齊規(guī)則:

成員對齊規(guī)則

針對一個(gè)基礎(chǔ)類型變量,如果 unsafe.AlignOf() 返回的值是 m,那么該變量的地址需要 被m整除 (如果當(dāng)前地址不能整除,填充空白字節(jié),直至可以整除)。

整體對齊規(guī)則

針對一個(gè)結(jié)構(gòu)體,如果 unsafe.AlignOf() 返回值是 m,需要保證該結(jié)構(gòu)體整體內(nèi)存占用是 m的整數(shù)倍,如果當(dāng)前不是整數(shù)倍,需要在后面填充空白字節(jié)。

通過內(nèi)存對齊后,就可以保證在訪問一個(gè)變量地址時(shí):

  • 如果該變量占用內(nèi)存小于字長:保證一次訪問就能得到數(shù)據(jù);
  • 如果該變量占用內(nèi)存大于字長:保證第一次內(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)存對齊需保證變量首地址可以被4整除,我們假設(shè)地址從0開始,0可以被4整除:

成員變量1內(nèi)存對齊

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)存對齊

3.第三個(gè)字段是 int32 類型,unsafe.Sizeof(int32(1))=4,內(nèi)存占用為4個(gè)字節(jié),同時(shí)unsafe.Alignof(int32(1)) = 4,內(nèi)存對齊需保證變量首地址可以被4整除,當(dāng)前地址為16,16可以被4整除:

成員變量3內(nèi)存對齊

4.所有成員對齊都已經(jīng)完成,現(xiàn)在我們需要看一下整體對齊規(guī)則:unsafe.Alignof(A{}) = 8,即三個(gè)變量成員的最大值,內(nèi)存對齊需要保證該結(jié)構(gòu)體的內(nèi)存占用是 8 的整數(shù)倍,當(dāng)前內(nèi)存占用是 20個(gè)字節(jié),因此需要再補(bǔ)充4個(gè)字節(jié):

整體對齊

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)存對齊需保證變量首地址可以被4整除,我們假設(shè)地址從0開始,0可以被4整除:

成員變量1內(nèi)存對齊

2.第二個(gè)字段是 int32 類型,unsafe.Sizeof(int32(1))=4,內(nèi)存占用為4個(gè)字節(jié),同時(shí)unsafe.Alignof(int32(1)) = 4,內(nèi)存對齊需保證變量首地址可以被4整除,當(dāng)前地址為4,4可以被4整除:

成員變量2內(nèi)存對齊

3.第三個(gè)字段是 int64 類型,unsafe.Sizeof(int64(1))=8,內(nèi)存占用為8個(gè)字節(jié),同時(shí)unsafe.Alignof(int64(1)) = 8,內(nèi)存對齊需保證變量首地址可以被8整除,當(dāng)前地址為8,8可以被8整除:

成員變量3內(nèi)存對齊

4.所有成員對齊都已經(jīng)完成,現(xiàn)在我們需要看一下整體對齊規(guī)則:unsafe.Alignof(B{}) = 8,即三個(gè)變量成員的最大值,內(nèi)存對齊需要保證該結(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)體的對齊規(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)存對齊,內(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é)

本篇文章我們一起學(xué)習(xí)了Go 語言中的內(nèi)存對齊,主要內(nèi)容如下:

  • unsafe.Sizeof(x) 返回了變量x的內(nèi)存占用大小
  • 兩個(gè)結(jié)構(gòu)體,即使包含變量類型的數(shù)量相同,但是位置不同,占用的內(nèi)存大小也不同,由此引出了內(nèi)存對齊
  • 內(nèi)存對齊包含成員對齊和整體對齊,與 unsafe.AlignOf(x) 息息相關(guān)
  • 空結(jié)構(gòu)體作為成員變量時(shí),是否占用內(nèi)存和所處位置有關(guān)
  • 在實(shí)際開發(fā)中,我們可以通過調(diào)整變量位置,優(yōu)化內(nèi)存占用(一般按照變量內(nèi)存大小順序排列,整體占用內(nèi)存更?。?/li>

以上就是詳解Go語言中的內(nèi)存對齊的詳細(xì)內(nèi)容,更多關(guān)于Go語言內(nèi)存對齊的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • GO語言基礎(chǔ)庫os包的函數(shù)全面解析

    GO語言基礎(chǔ)庫os包的函數(shù)全面解析

    這篇文章主要為大家介紹了GO語言基礎(chǔ)庫os包的函數(shù)全面解析, 有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Go/C語言LeetCode題解997找到小鎮(zhèn)法官

    Go/C語言LeetCode題解997找到小鎮(zhèn)法官

    這篇文章主要為大家介紹了Go語言LeetCode題解997找到小鎮(zhèn)的法官示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • Golang庫插件注冊加載機(jī)制的問題

    Golang庫插件注冊加載機(jī)制的問題

    這篇文章主要介紹了Golang庫插件注冊加載機(jī)制,這里說的插件并不是指的golang原生的可以在buildmode中加載指定so文件的那種加載機(jī)制,需要的朋友可以參考下
    2022-03-03
  • golang利用unsafe操作未導(dǎo)出變量-Pointer使用詳解

    golang利用unsafe操作未導(dǎo)出變量-Pointer使用詳解

    這篇文章主要給大家介紹了關(guān)于golang利用unsafe操作未導(dǎo)出變量-Pointer使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08
  • Go語言實(shí)現(xiàn)JSON解析的方法詳解

    Go語言實(shí)現(xiàn)JSON解析的方法詳解

    在日常項(xiàng)目中,使用Json格式進(jìn)行數(shù)據(jù)封裝是比較常見的操作。本文將詳細(xì)講解如何利用Go語言實(shí)現(xiàn)JSON的解析,感興趣的小伙伴可以學(xué)習(xí)一下
    2022-04-04
  • Golang線上內(nèi)存爆掉問題排查(pprof)與解決

    Golang線上內(nèi)存爆掉問題排查(pprof)與解決

    這篇文章主要介紹了Golang線上內(nèi)存爆掉問題排查(pprof)與解決,涉及到數(shù)據(jù)敏感,文中代碼是我模擬線上故障的一個(gè)情況,好在我們程序都有添加pprof監(jiān)控,于是直接通過go tool pprof分析,需要的朋友可以參考下
    2024-04-04
  • golang優(yōu)先級隊(duì)列的實(shí)現(xiàn)全過程

    golang優(yōu)先級隊(duì)列的實(shí)現(xiàn)全過程

    優(yōu)先級隊(duì)列是一種特殊隊(duì)列,下面這篇文章主要給大家介紹了關(guān)于golang優(yōu)先級隊(duì)列的實(shí)現(xiàn)全過程,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-12-12
  • go語言寫的簡要數(shù)據(jù)同步工具詳解

    go語言寫的簡要數(shù)據(jù)同步工具詳解

    作為go-etl工具的作者,想要安利一下這個(gè)小巧的數(shù)據(jù)同步工具,它在同步百萬級別的數(shù)據(jù)時(shí)表現(xiàn)極為優(yōu)異,基本能在幾分鐘完成數(shù)據(jù)同步,這篇文章主要介紹了go語言寫的簡要數(shù)據(jù)同步工具,需要的朋友可以參考下
    2024-07-07
  • go原子級內(nèi)存操作實(shí)現(xiàn)

    go原子級內(nèi)存操作實(shí)現(xiàn)

    原子級內(nèi)存操作是在多線程并發(fā)執(zhí)行時(shí),能夠確保某個(gè)內(nèi)存操作是不可中斷的操作,本文主要介紹了go原子級內(nèi)存操作實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • 關(guān)于Golang標(biāo)準(zhǔn)庫flag的全面講解

    關(guān)于Golang標(biāo)準(zhǔn)庫flag的全面講解

    這篇文章主要介紹了關(guān)于Golang標(biāo)準(zhǔn)庫flag的全面講解,這個(gè)庫的代碼量只有1000行左右,卻提供了非常完善的命令行參數(shù)解析功能,更多相關(guān)內(nèi)容需要的朋友可以參考一下
    2022-09-09

最新評論

宁南县| 铜川市| 军事| 弥渡县| 岱山县| 嵩明县| 平和县| 宁都县| 武鸣县| 新泰市| 尼勒克县| 横山县| 阿拉善盟| 苏尼特右旗| 岐山县| 汨罗市| 天门市| 灵丘县| 驻马店市| 临泉县| 神池县| 凌云县| 武平县| 阳西县| 古丈县| 安新县| 陇南市| 郎溪县| 都江堰市| 宜阳县| 神池县| 寻乌县| 应城市| 宁国市| 齐齐哈尔市| 泽普县| 榆树市| 内江市| 新邵县| 禹州市| 嘉禾县|