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

Go語言中的內(nèi)存布局詳解

 更新時(shí)間:2016年11月17日 11:21:43   投稿:daisy  
這篇文章主要給大家介紹了Go語言中的內(nèi)存布局,那么本文中將嘗試解釋Go如何在內(nèi)存中構(gòu)建結(jié)構(gòu)體,以及結(jié)構(gòu)體在字節(jié)和比特位方面是什么樣子。 有需要的朋友們可以參考借鑒,感興趣的朋友們下面來跟著小編一起學(xué)習(xí)學(xué)習(xí)吧。

一、go語言內(nèi)存布局

想象一下,你有一個(gè)如下的結(jié)構(gòu)體。

復(fù)制代碼 代碼如下:

type MyData struct {
        aByte   byte
        aShort  int16
        anInt32 int32
        aSlice  []byte
}

那么這個(gè)結(jié)構(gòu)體究竟是什么呢? 從根本上說,它描述了如何在內(nèi)存中布局?jǐn)?shù)據(jù)。 這是什么意思?編譯器又是如何展現(xiàn)出來呢? 我們來看一下。 首先讓我們使用反射來檢查結(jié)構(gòu)中的字段。

二、反射之上

下面是一些使用反射來找出字段大小及其偏移量(它們相對(duì)于結(jié)構(gòu)的開始位于內(nèi)存中的位置)的代碼。 反射可以告訴我們編譯器是怎么看待類型(包括結(jié)構(gòu))的。

復(fù)制代碼 代碼如下:

// First ask Go to give us some information about the MyData type
typ := reflect.TypeOf(MyData{})
fmt.Printf("Struct is %d bytes long\n", typ.Size())
// We can run through the fields in the structure in order
n := typ.NumField()
for i := 0; i < n; i++ {
        field := typ.Field(i)
        fmt.Printf("%s at offset %v, size=%d, align=%d\n",
            field.Name, field.Offset, field.Type.Size(),
            field.Type.Align())
 }

除了每個(gè)字段的偏移和大小,我還打印了每個(gè)字段的對(duì)齊方式,我稍后會(huì)解釋。結(jié)果如下:

復(fù)制代碼 代碼如下:

Struct is 32 bytes long
aByte at offset 0, size=1, align=1
aShort at offset 2, size=2, align=2
anInt32 at offset 4, size=4, align=4
aSlice at offset 8, size=24, align=8

aByte是我們結(jié)構(gòu)體中的第一個(gè)字段,偏移量為0.它使用1字節(jié)的內(nèi)存。

aShort是第二個(gè)字段。它使用2字節(jié)的內(nèi)存。奇怪的是偏移量是2。這是為什么呢?答案是對(duì)齊, CPU更好地訪問位于2字節(jié)(“2字節(jié)邊界”)的倍數(shù)的地址處的2個(gè)字節(jié),并訪問位于4字節(jié)邊界上的4個(gè)字節(jié),直到CPU的自然整數(shù)大小,在現(xiàn)代CPU上是8字節(jié)(64位)。

在一些較舊的RISC CPU訪問錯(cuò)誤對(duì)齊的數(shù)字引起一個(gè)故障:在一些UNIX系統(tǒng)上,這將是一個(gè)SIGBUS,它會(huì)停止你的程序(或內(nèi)核)。一些系統(tǒng)能夠處理這些錯(cuò)誤并修復(fù)錯(cuò)誤:您的代碼將運(yùn)行,但會(huì)緩慢的運(yùn)行,因?yàn)轭~外的代碼將由操作系統(tǒng)運(yùn)行以修復(fù)錯(cuò)誤。我相信英特爾和ARM的CPU也只是處理芯片上的任何不對(duì)齊:也許我們將在以后的文章中測(cè)試這一點(diǎn),以及任何性能的影響。

無論如何,對(duì)齊是Go編譯器跳過一個(gè)字節(jié)放置字段aShort以便它位于2字節(jié)邊界的原因。因?yàn)檫@樣,我們可以將另一個(gè)字段放進(jìn)結(jié)構(gòu)體中,而不使它占用更大內(nèi)存。這里是我們的結(jié)構(gòu)的新版本,在aByte之后立即有一個(gè)新字段anotherByte。

復(fù)制代碼 代碼如下:

type MyData struct {
       aByte       byte
       anotherByte byte
       aShort      int16
       anInt32     int32
       aSlice      []byte
}

我們?cè)俅芜\(yùn)行反射代碼,可以看到anotherByte正好在aByte和aShort之間的空閑空間。 它坐落在偏移1,aShort仍然在偏移2.現(xiàn)在可能是時(shí)候注意我之前提到的那個(gè)神秘對(duì)齊字段。 它告訴我們和Go編譯器,這個(gè)字段需要如何對(duì)齊。

復(fù)制代碼 代碼如下:

Struct is 32 bytes long
aByte at offset 0, size=1, align=1
anotherByte at offset 1, size=1, align=1
aShort at offset 2, size=2, align=2
anInt32 at offset 4, size=4, align=4
aSlice at offset 8, size=24, align=8

三、看看內(nèi)存

然而我們的結(jié)構(gòu)體在內(nèi)存中到底是什么樣子? 讓我們看看我們能不能找到答案。 首先讓我們構(gòu)建一個(gè)MyData實(shí)例,并填充一些值。我選擇了應(yīng)該容易在內(nèi)存中找到的值。

復(fù)制代碼 代碼如下:

data := MyData{
        aByte:   0x1,
        aShort:  0x0203,
        anInt32: 0x04050607,
        aSlice:  []byte{
                0x08, 0x09, 0x0a,
        },
 }

現(xiàn)在一些代碼訪問組成這個(gè)結(jié)構(gòu)的字節(jié)。 我們想要獲取這個(gè)結(jié)構(gòu)的實(shí)例,在內(nèi)存中找到它的地址,并打印出該內(nèi)存中的字節(jié)。

我們使用unsafe包來幫助我們這樣做。 這讓我們繞過Go類型系統(tǒng)將指向我們的結(jié)構(gòu)的指針轉(zhuǎn)換為32字節(jié)數(shù)組,這個(gè)數(shù)組就是組成我們的結(jié)構(gòu)體的內(nèi)存數(shù)據(jù)。

復(fù)制代碼 代碼如下:

dataBytes := (*[32]byte)(unsafe.Pointer(&data))
fmt.Printf("Bytes are %#v\n", dataBytes)

我們運(yùn)行以上代碼。 這是結(jié)果,第一個(gè)字段,aByte,從我們的結(jié)構(gòu)中以粗體顯示。 這是希望你期望的,單字節(jié)aByte = 0x01在偏移0。

復(fù)制代碼 代碼如下:

Bytes are &[32]uint8{**0x1**, 0x0, 0x3, 0x2, 0x7, 0x6, 0x5, 0x4, 0x5a, 0x5, 0x1, 0x20, 0xc4, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}

接下來我們來看看AShort。 這是在偏移量2的位置并且長(zhǎng)度為2.如果你記得,aShort = 0x0203,但數(shù)據(jù)顯示的字節(jié)是倒序。 這是因?yàn)榇蠖鄶?shù)現(xiàn)代CPU都是Little-Endian:該值的最低位字節(jié)首先出現(xiàn)在內(nèi)存中。

復(fù)制代碼 代碼如下:

Bytes are &[32]uint8{0x1, 0x0, **0x3, 0x2**, 0x7, 0x6, 0x5, 0x4, 0x5a, 0x5, 0x1, 0x20, 0xc4, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}

同樣的事情發(fā)生在Int32 = 0x04050607。 最低位字節(jié)首先出現(xiàn)在內(nèi)存中。

復(fù)制代碼 代碼如下:

Bytes are &[32]uint8{0x1, 0x0, 0x3, 0x2, **0x7, 0x6, 0x5, 0x4**, 0x5a, 0x5, 0x1, 0x20, 0xc4, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}

四、神秘的插曲

現(xiàn)在我們看到什么? 這是aSlice = [] byte {0x08,0x09,0x0a} ,在偏移量8的24個(gè)字節(jié)。我沒有看到我的序列0x08,0x09,0x0a的任何地方的任何符號(hào)。 這是怎么回事?

復(fù)制代碼 代碼如下:

Bytes are &[32]uint8{0x1, 0x0, 0x3, 0x2, 0x7, 0x6, 0x5, 0x4, **0x5a, 0x5, 0x1, 0x20, 0xc4, 0x0, 0x0, 0x0, 0x3, 0x0**, **0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0**}

Go反射包里自有答案。 slice在Go語言中由以下結(jié)構(gòu)體表示,該結(jié)構(gòu)從指針數(shù)據(jù)開始,該數(shù)據(jù)指向保存切片中的數(shù)據(jù)的存儲(chǔ)器; 然后是該存儲(chǔ)器中的有用數(shù)據(jù)的長(zhǎng)度Len,以及該存儲(chǔ)器的大小Cap。

復(fù)制代碼 代碼如下:

type SliceHeader struct {
        Data uintptr
        Len  int
        Cap  int
}

如果把它提供給我們的代碼,我們得到以下偏移和大小。 數(shù)據(jù)指針和兩個(gè)長(zhǎng)度各為8個(gè)字節(jié),具有8個(gè)字節(jié)對(duì)齊。

復(fù)制代碼 代碼如下:

Struct is 24 bytes long
Data at offset 0, size=8, align=8
Len at offset 8, size=8, align=8
Cap at offset 16, size=8, align=8

如果我們?cè)倏匆幌潞竺娴膬?nèi)存結(jié)構(gòu),我們可以看到數(shù)據(jù)是在地址0x000000c42001055a。 之后,我們看到Len和Cap都是3,這是我們的數(shù)據(jù)的長(zhǎng)度。

復(fù)制代碼 代碼如下:

Bytes are &[32]uint8{0x1, 0x0, 0x3, 0x2, 0x7, 0x6, 0x5, 0x4, **0x5a, 0x5, 0x1, 0x20, 0xc4, 0x0, 0x0, 0x0**, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}

我們可以直接用以下代碼訪問這些數(shù)據(jù)字節(jié)。 首先讓我們直接訪問slice頭,然后打印出數(shù)據(jù)指向的內(nèi)存。

復(fù)制代碼 代碼如下:

dataslice := *(*reflect.SliceHeader)(unsafe.Pointer(&data.aSlice))
fmt.Printf("Slice data is %#v\n",
        (*[3]byte)(unsafe.Pointer(dataslice.Data)))

這是輸出:

復(fù)制代碼 代碼如下:

Slice data is &[3]uint8{0x8, 0x9, 0xa}

總結(jié)

以上就是關(guān)于Go語言內(nèi)存布局的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家學(xué)習(xí)或者使用Go語言能有所幫助,如果有疑問大家可以留言交流。

相關(guān)文章

  • 使用Golang搭建web服務(wù)的實(shí)現(xiàn)步驟

    使用Golang搭建web服務(wù)的實(shí)現(xiàn)步驟

    本文主要介紹了使用Golang搭建web服務(wù)的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • golang利用pprof與go-torch如何做性能分析

    golang利用pprof與go-torch如何做性能分析

    這篇文章主要給大家介紹了關(guān)于golang利用pprof與go-torch如何做性能分析的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • Go語言應(yīng)用閉包之返回函數(shù)

    Go語言應(yīng)用閉包之返回函數(shù)

    這篇文章主要介紹了Go語言應(yīng)用閉包之返回函數(shù),對(duì)于非常底層的純 Go 語言代碼或者包而言,在各個(gè)操作系統(tǒng)平臺(tái)上的可移植性是非常強(qiáng)的,只需要將源碼拷貝到相應(yīng)平臺(tái)上進(jìn)行編譯即可,或者可以使用交叉編譯來構(gòu)建目標(biāo)平臺(tái)的應(yīng)用程序,需要的朋友可以參考下
    2023-07-07
  • go語言用八百行代碼實(shí)現(xiàn)一個(gè)JSON解析器

    go語言用八百行代碼實(shí)現(xiàn)一個(gè)JSON解析器

    這篇文章主要為大家介紹了go語言用八百行代碼實(shí)現(xiàn)一個(gè)JSON解析器實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • golang使用接口進(jìn)行靈活緩存

    golang使用接口進(jìn)行靈活緩存

    緩存是編程中一種常見的技術(shù),通過存儲(chǔ)昂貴的計(jì)算或?IO?結(jié)果來快速查找,從而提高性能,本文將介紹一下Go的接口如何幫助構(gòu)建靈活、可擴(kuò)展的緩存,感興趣的可以了解下
    2023-09-09
  • golang語言中for循環(huán)語句用法實(shí)例

    golang語言中for循環(huán)語句用法實(shí)例

    這篇文章主要介紹了golang語言中for循環(huán)語句用法,實(shí)例分析了for循環(huán)遍歷的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-01-01
  • Go語言入門之基礎(chǔ)語法和常用特性解析

    Go語言入門之基礎(chǔ)語法和常用特性解析

    這篇文章主要給大家講解了Go語言的基礎(chǔ)語法和常用特性解析,比較適合入門小白,文中通過代碼示例介紹的非常詳細(xì),對(duì)我們學(xué)習(xí)Go語言有一定的幫助,需要的朋友可以參考下
    2023-07-07
  • Go語言輕量級(jí)線程Goroutine用法實(shí)例

    Go語言輕量級(jí)線程Goroutine用法實(shí)例

    這篇文章主要介紹了Go語言輕量級(jí)線程Goroutine用法,實(shí)例分析了goroutine使用技巧,需要的朋友可以參考下
    2015-02-02
  • 特殊字符的json序列化總結(jié)大全

    特殊字符的json序列化總結(jié)大全

    這篇文章主要給大家介紹了關(guān)于特殊字符的json序列化的相關(guān)資料,通過示例代碼分別給大家介紹了關(guān)于python 、 rust 、 java 和golang對(duì)特殊字符的json序列化操作,需要的朋友可以參考借鑒,下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-09-09
  • 詳解Go語言的context包從放棄到入門

    詳解Go語言的context包從放棄到入門

    這篇文章主要介紹了Go語言的context包從放棄到入門,本文通過實(shí)例演示給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12

最新評(píng)論

阳江市| 乌鲁木齐县| 龙川县| 辽中县| 滨州市| 辽中县| 宁南县| 玉山县| 新巴尔虎右旗| 桑植县| 砀山县| 贡嘎县| 长乐市| 墨脱县| 根河市| 新蔡县| 化德县| 阿尔山市| 英吉沙县| 南康市| 五华县| 大足县| 神木县| 夏河县| 阿克| 赤峰市| 丰宁| 朝阳市| 乌恰县| 平谷区| 仪征市| 奉贤区| 定边县| 西充县| 信丰县| 澜沧| 沾益县| 英德市| 青浦区| 田东县| 克东县|