go 結(jié)構(gòu)體 內(nèi)存對齊

在組內(nèi)學(xué)習(xí)go語言規(guī)范時,學(xué)習(xí)到了一個很有意思并且能減少內(nèi)存分配提高性能的東西吩翻,先通過一個簡單的問題向大家展示一下~

type Example struct {
    Bool bool
    Int  int32
    Long int64
}

Example 結(jié)構(gòu)體中 bool 占1個字節(jié),int32占4個字節(jié),int64占8個字節(jié)洪乍,實(shí)際上這個結(jié)構(gòu)體一個對象總共占用了16個字節(jié),可以通過 unsafe.Sizeof()函數(shù)來查看所占內(nèi)存大小夜焦。

type Example struct {
    Bool bool
    Long int64
    Int  int32
}

那我在調(diào)換一下int 和 long 的位置壳澳,這個結(jié)構(gòu)體的一個對象就占用24個字節(jié)了,是不是很有意思茫经,接下來就引出了go 語言內(nèi)存對齊的話題

內(nèi)存對齊

為了最大限度地減少內(nèi)存碎片整理巷波,go在分配內(nèi)存時都會將內(nèi)存邊界對齊。要確定 Go 在體系結(jié)構(gòu)上所用的對齊邊界卸伞,你可以運(yùn)行 unsafe.Alignof 函數(shù)抹镊。Go 在 64 位系統(tǒng)的對齊邊界是 8 個字節(jié)。因此在 Go 確定我們結(jié)構(gòu)體的內(nèi)存分配時荤傲,它將填充字節(jié)以確保最終占用的內(nèi)存是 8 的倍數(shù)垮耳。unsafe.Offsetof取結(jié)構(gòu)體字段內(nèi)存的offset值。下面通過Example 這個結(jié)構(gòu)體來具體展示下

type Example struct {
    Bool bool
    Int  int32
    Long int64
}
example := Example{}
alignmentBoundary := unsafe.Alignof(example)

    fmt.Printf("Example Alignof Size : %d\n", alignmentBoundary)

    sizeBool := unsafe.Sizeof(example.Bool)
    offsetBool := unsafe.Offsetof(example.Bool)
    fmt.Printf("BoolValue = Size: %d Offset: %d Addr: %v\n", sizeBool, offsetBool, &example.Bool)

    sizeInt := unsafe.Sizeof(example.Int)
    offsetInt := unsafe.Offsetof(example.Int)
    fmt.Printf("IntValue = Size: %d Offset: %d Addr: %v\n", sizeInt, offsetInt, &example.Int)

    sizeLong := unsafe.Sizeof(example.Long)
    offsetLong := unsafe.Offsetof(example.Long)

    fmt.Printf("LongValue = Size: %d Offset: %d Addr: %v\n", sizeLong, offsetLong, &example.Long)
    
    fmt.Printf("Example SizeOf : %d\n",unsafe.Sizeof(example))

以上代碼的輸出結(jié)果為

Example Alignof Size : 8
BoolValue = Size: 1 Offset: 0 Addr: 0xc0000a4030
IntValue = Size: 4 Offset: 4 Addr: 0xc0000a4034
LongValue = Size: 8 Offset: 8 Addr: 0xc0000a4038
Example SizeOf : 16

bool 和 int 占用了8個字節(jié)弃酌,在8個字節(jié)中 offset 1-4 之間go填充了內(nèi)存氨菇,long正好占用了8個字節(jié)。通過unsafe.Sizeof 看到結(jié)構(gòu)體占了16個字節(jié)妓湘,再將結(jié)構(gòu)體中的int 和 long 調(diào)一下位置查蓉,會有意想不到的效果。

type Example struct {
    Bool bool
    Long int64
    Int  int32
}
example := Example{}
alignmentBoundary := unsafe.Alignof(example)

    fmt.Printf("Example Alignof Size : %d\n", alignmentBoundary)

    sizeBool := unsafe.Sizeof(example.Bool)
    offsetBool := unsafe.Offsetof(example.Bool)
    fmt.Printf("BoolValue = Size: %d Offset: %d Addr: %v\n", sizeBool, offsetBool, &example.Bool)

    sizeLong := unsafe.Sizeof(example.Long)
    offsetLong := unsafe.Offsetof(example.Long)

    fmt.Printf("LongValue = Size: %d Offset: %d Addr: %v\n", sizeLong, offsetLong, &example.Long)

    sizeInt := unsafe.Sizeof(example.Int)
    offsetInt := unsafe.Offsetof(example.Int)
    fmt.Printf("IntValue = Size: %d Offset: %d Addr: %v\n", sizeInt, offsetInt, &example.Int)

    fmt.Printf("Example SizeOf : %d\n",unsafe.Sizeof(example))

以上輸出結(jié)果為

Example Alignof Size : 8
BoolValue = Size: 1 Offset: 0 Addr: 0xc00001c0e0
LongValue = Size: 8 Offset: 8 Addr: 0xc00001c0e8
IntValue = Size: 4 Offset: 16 Addr: 0xc00001c0f0
Example SizeOf : 24

說明:由于bool 占用1個字節(jié)榜贴,long占用8個字節(jié)豌研,第一個對齊邊界還剩4字節(jié)放不下long類型的8個字節(jié)妹田,因此會重新開辟一個邊界8字節(jié),long正好占滿鹃共,剩下的int 在開辟一個8字節(jié)鬼佣,剩余的go會把內(nèi)存填充。所以Example 占用了24個字節(jié)霜浴。因此在定義結(jié)構(gòu)體時晶衷,還是要考慮字段順序問題,這樣能減少不必要的內(nèi)存分配阴孟,減少gc晌纫,在流量大的情況下能提高系統(tǒng)性能

unsafe 內(nèi)存操作

借助unsafe.Pointer 我們可以拿到結(jié)構(gòu)體中字段的指針,然后修改它的值永丝,舉例如下

e := &Example{
        Bool: true,
        Int:  1,
        Long: 1,
    }
    fmt.Printf("Example before value %+v \n",e)
    pointer := unsafe.Pointer(e)
    // bool 設(shè)置值
    b := (*bool)(pointer)
    *b = false
    // int 設(shè)置值
    i := (*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(e)) + unsafe.Offsetof(e.Int)))
    *i = 100

    l := (*int64)(unsafe.Pointer(uintptr(unsafe.Pointer(e)) + 8))
    *l = 1000
    fmt.Printf("Example after value %+v \n",e)

輸出結(jié)果如下

Example before value &{Bool:true Int:1 Long:1} 
Example after value &{Bool:false Int:100 Long:1000} 

當(dāng)結(jié)構(gòu)體的某些字段是小寫锹漱,并且被別的包引用時,只要能拿到這個結(jié)構(gòu)體的實(shí)例慕嚷,算好字段的offset就可以通過Pointer修改結(jié)構(gòu)體字段的值哥牍,感覺還是很有意思的~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市喝检,隨后出現(xiàn)的幾起案子嗅辣,更是在濱河造成了極大的恐慌,老刑警劉巖蛇耀,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辩诞,死亡現(xiàn)場離奇詭異坎弯,居然都是意外死亡纺涤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門抠忘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來撩炊,“玉大人,你說我怎么就攤上這事崎脉∨】龋” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵囚灼,是天一觀的道長骆膝。 經(jīng)常有香客問我,道長灶体,這世上最難降的妖魔是什么阅签? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蝎抽,結(jié)果婚禮上政钟,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好养交,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布精算。 她就那樣靜靜地躺著,像睡著了一般碎连。 火紅的嫁衣襯著肌膚如雪灰羽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天鱼辙,我揣著相機(jī)與錄音谦趣,去河邊找鬼。 笑死座每,一個胖子當(dāng)著我的面吹牛前鹅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播峭梳,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼舰绘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了葱椭?” 一聲冷哼從身側(cè)響起捂寿,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎孵运,沒想到半個月后秦陋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡治笨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年驳概,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旷赖。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡顺又,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出等孵,到底是詐尸還是另有隱情稚照,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布俯萌,位于F島的核電站果录,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏咐熙。R本人自食惡果不足惜弱恒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望糖声。 院中可真熱鬧斤彼,春花似錦分瘦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至并扇,卻和暖如春去团,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背穷蛹。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工土陪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人肴熏。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓鬼雀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蛙吏。 傳聞我的和親對象是個殘疾皇子源哩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容