欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

golang內(nèi)存對齊詳解

 更新時(shí)間:2023年10月16日 08:51:58   作者:寫代碼的lorre  
在golang中,每一種數(shù)據(jù)類型都有其對應(yīng)的數(shù)據(jù)類型大小,也就是占用了多少內(nèi)存空間,我們可以通過unsafe.Sizeof函數(shù),來確定一個(gè)變量占用的內(nèi)存字節(jié)數(shù),本文將詳細(xì)給大家介紹golang內(nèi)存對齊,需要的朋友可以參考下

背景

在golang中,每一種數(shù)據(jù)類型都有其對應(yīng)的數(shù)據(jù)類型大小,也就是占用了多少內(nèi)存空間

我們可以通過unsafe.Sizeof函數(shù),來確定一個(gè)變量占用的內(nèi)存字節(jié)數(shù)

demo:

package main
import (
   "fmt"
   "testing"
   "unsafe"
)
func TestTypeSize(t *testing.T) {
   var a int8 = 4
   s1 := "hello world"
   s2 := "hahaha"
   fmt.Println(unsafe.Sizeof(a))  // 1字節(jié)
   fmt.Println(unsafe.Sizeof(s1)) // 16字節(jié)
   fmt.Println(unsafe.Sizeof(s2)) // 16字節(jié)
}

注意:

  • unsafe.Sizeof返回的是一個(gè)變量占用的內(nèi)存字節(jié)數(shù),而不是變量所表示內(nèi)容占用的內(nèi)存字節(jié)數(shù)。所以在上述demo中,盡管s1和s2的字符串內(nèi)容不一樣,但s1和s2的變量類型都是string,所以s1和s2占用的內(nèi)存字節(jié)數(shù)相同

結(jié)構(gòu)體大小

我們還可以通過unsafe.Sizeof來獲取結(jié)構(gòu)體占用的內(nèi)存字節(jié)數(shù)

demo1:

package main
import (
   "fmt"
   "testing"
   "unsafe"
)
type demo1 struct {
   a int8
   b int16
}
func TestStructSize1(t *testing.T) {
   d1 := demo1{}
   fmt.Println(unsafe.Sizeof(d1.a)) // 1字節(jié)
   fmt.Println(unsafe.Sizeof(d1.b)) // 2字節(jié)
   fmt.Println(unsafe.Sizeof(d1))   // 4字節(jié)
}

問題1:

  • 結(jié)構(gòu)體占用的內(nèi)存字節(jié)數(shù)不等于結(jié)構(gòu)體內(nèi)各個(gè)字段占用的內(nèi)存字節(jié)數(shù)之和。結(jié)構(gòu)體內(nèi)各個(gè)字段占用的內(nèi)存字節(jié)數(shù)之和為:3字節(jié) = 1字節(jié)(a占用字節(jié)數(shù)) + 2字節(jié)(b占用字節(jié)數(shù)),結(jié)構(gòu)體占用字節(jié)數(shù)為4字節(jié)

demo2:

package main
import (
   "fmt"
   "testing"
   "unsafe"
)
type demo2 struct {
   a int8
   b int16
   c int32
   d int64
}
type demo3 struct {
   a int8
   d int64
   b int16
   c int32
}
func TestStructSize2(t *testing.T) {
   d2 := demo2{}
   fmt.Println(unsafe.Sizeof(d2.a)) // 1字節(jié)
   fmt.Println(unsafe.Sizeof(d2.b)) // 2字節(jié)
   fmt.Println(unsafe.Sizeof(d2.c)) // 4字節(jié)
   fmt.Println(unsafe.Sizeof(d2.d)) // 8字節(jié)
   fmt.Println(unsafe.Sizeof(d2))   // 16字節(jié)
   d3 := demo3{}
   fmt.Println(unsafe.Sizeof(d3.a)) // 1字節(jié)
   fmt.Println(unsafe.Sizeof(d3.b)) // 2字節(jié)
   fmt.Println(unsafe.Sizeof(d3.c)) // 4字節(jié)
   fmt.Println(unsafe.Sizeof(d3.d)) // 8字節(jié)
   fmt.Println(unsafe.Sizeof(d3))   // 24字節(jié)
}

問題2:

  • 當(dāng)兩個(gè)結(jié)構(gòu)體內(nèi)的字段類型一樣時(shí),字段順序不同時(shí),結(jié)構(gòu)體占用的字節(jié)數(shù)也可能不同

出現(xiàn)上面兩個(gè)問題的根本原因,就是本文要討論的內(nèi)容:內(nèi)存對齊

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

現(xiàn)代計(jì)算機(jī)中內(nèi)存空間都是按照byte劃分的,從理論上講似乎對任何類型的變量的訪問可以從任何地址開始,但實(shí)際情況是在訪問特定變量的時(shí)候經(jīng)常在特定的內(nèi)存地址訪問,這就需要各類型數(shù)據(jù)按照一定的規(guī)則在空間上排列,而不是順序的一個(gè)接一個(gè)的排放,這就是對齊

簡單來說,內(nèi)存對齊就是把各種數(shù)據(jù)類型按照一定的規(guī)則,在內(nèi)存空間進(jìn)行排列,而不是直接按照順序進(jìn)行排列

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

那么為什么需要進(jìn)行內(nèi)存對齊呢,主要原因有以下幾點(diǎn):

  • 性能原因:CPU為了加速對內(nèi)存的訪問速度,并不是一個(gè)字節(jié)一個(gè)字節(jié)的去訪問內(nèi)存,而是一次訪問多個(gè)字節(jié),一般稱之為字長。32位CUP的字長一般是4字節(jié),64位CPU的字長一般是8字節(jié)。如果沒有進(jìn)行內(nèi)存對齊,那么CPU在訪問一個(gè)變量時(shí),可能需要進(jìn)行多次讀取,然后進(jìn)行拼接,才能得到最終的變量內(nèi)容。進(jìn)行內(nèi)存對齊后,可以減少CPU訪問內(nèi)存次數(shù),提升性能

  • 更好的保證訪問的原子性

  • 平臺原因(移植原因):不是所有的硬件平臺都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常

如何進(jìn)行內(nèi)存對齊

常見數(shù)據(jù)類型的內(nèi)存對齊

編譯器按照每種數(shù)據(jù)類型的對齊邊界來進(jìn)行內(nèi)存對齊

首先需要確認(rèn)每種數(shù)據(jù)類型的對齊邊界,對齊邊界 = min(類型大小,平臺字長)

常見數(shù)據(jù)類型在常見平臺上的對齊邊界:

類型類型大小64位平臺字長64位平臺對齊邊界32位平臺字長32位平臺對齊邊界
int81byte8byte1byte4byte1byte
int162byte8byte2byte4byte2byte
int324byte8byte4byte4byte4byte
int648byte8byte8byte4byte4byte
string16byte8byte8byte4byte4byte

為什么對齊邊界需要取類型大小和平臺字長的最小值呢?

答案是為了節(jié)省內(nèi)存,避免內(nèi)存浪費(fèi),提升讀取的性能

  • 當(dāng)類型大小 < 平臺字長時(shí),int8類型在64位平臺進(jìn)行內(nèi)存對齊兩種情況如圖:

  • 當(dāng)類型大小 > 平臺字長時(shí),int64類型在32位平臺進(jìn)行內(nèi)存對齊兩種情況如圖:

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

結(jié)構(gòu)體的對齊邊界為:結(jié)構(gòu)體內(nèi)成員類型最大的對齊邊界

結(jié)構(gòu)體進(jìn)行內(nèi)存對齊的兩個(gè)要求:

  • 起始地址是結(jié)構(gòu)體對齊邊界的倍數(shù)
  • 結(jié)構(gòu)體整體占用字節(jié)數(shù)必須是結(jié)構(gòu)體對齊邊界的倍數(shù)。為了保證結(jié)構(gòu)體數(shù)組的內(nèi)存對齊

下面我們具體分析下結(jié)構(gòu)體的內(nèi)存對齊

type demo4 struct {
   a int8
   b int64
   c int32
   d int16
}

首先確定結(jié)構(gòu)體demo4的對齊邊界為:成員類型最大的對齊邊界 = 8byte

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

結(jié)構(gòu)體內(nèi)存對齊的特殊情況

如果結(jié)構(gòu)體的字段包含空結(jié)構(gòu)體類型時(shí)

  • 空結(jié)構(gòu)體類型字段不是最后一個(gè)字段時(shí),不會占用內(nèi)存
  • 空結(jié)構(gòu)體類型字段是最后一個(gè)字段時(shí),需要進(jìn)行內(nèi)存對齊,占用的內(nèi)存大小和前一個(gè)字段的大小一致

demo:

package main
import (
   "fmt"
   "testing"
   "unsafe"
)
type demo5 struct {
   s struct{}
   a int8
}
type demo6 struct {
   a int8
   s struct{}
}
func TestStructSize3(t *testing.T) {
   d5 := demo5{}
   fmt.Println(unsafe.Sizeof(d5.a)) // 1字節(jié)
   fmt.Println(unsafe.Sizeof(d5.s)) // 0字節(jié)
   fmt.Println(unsafe.Sizeof(d5))   // 1字節(jié)
   d6 := demo6{}
   fmt.Println(unsafe.Sizeof(d6.a)) // 1字節(jié)
   fmt.Println(unsafe.Sizeof(d6.s)) // 0字節(jié)
   fmt.Println(unsafe.Sizeof(d6))   // 2字節(jié)
}

空結(jié)構(gòu)體類型字段是最后一個(gè)字段時(shí),需要占用內(nèi)存,主要還是為了解決內(nèi)存泄漏問題

內(nèi)存泄漏問題分析:

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

相關(guān)文章

  • Golang中slice切片的實(shí)現(xiàn)示例

    Golang中slice切片的實(shí)現(xiàn)示例

    Go語言中,切片是對數(shù)組的抽象,提供了更靈活的動態(tài)數(shù)組解決方案,本文就來介紹一下Golang中slice切片的實(shí)現(xiàn)示例,感興趣的可以了解一下
    2024-09-09
  • 詳解Go語言中的作用域和變量隱藏

    詳解Go語言中的作用域和變量隱藏

    這篇文章主要為大家介紹了Go語言中的作用域和變量隱藏,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Go語言有一定的幫助,感興趣的小伙伴可以了解一下
    2022-04-04
  • Go素?cái)?shù)篩選分析詳解

    Go素?cái)?shù)篩選分析詳解

    學(xué)習(xí)Go語言的過程中,遇到素?cái)?shù)篩選的問題。這是一個(gè)經(jīng)典的并發(fā)編程問題,是某大佬的代碼,短短幾行代碼就實(shí)現(xiàn)了素?cái)?shù)篩選,這篇文章主要介紹了Go素?cái)?shù)篩選分析,需要的朋友可以參考下
    2022-10-10
  • golang兩種調(diào)用rpc的方法

    golang兩種調(diào)用rpc的方法

    這篇文章主要介紹了golang兩種調(diào)用rpc的方法,結(jié)合實(shí)例形式分析了Go語言調(diào)用rpc的原理與實(shí)現(xiàn)方法,需要的朋友可以參考下
    2016-07-07
  • go語言算法題解二叉樹的最小深度

    go語言算法題解二叉樹的最小深度

    這篇文章主要為大家介紹了go語言算法題解二叉樹的最小深度示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • Go使用database/sql操作數(shù)據(jù)庫的教程指南

    Go使用database/sql操作數(shù)據(jù)庫的教程指南

    Go?語言中,有一個(gè)名為database/sql的標(biāo)準(zhǔn)庫,提供了統(tǒng)一的編程接口,使開發(fā)人員能夠以一種通用的方式與各種關(guān)系型數(shù)據(jù)庫進(jìn)行交互,本文就來和大家講講它的具體操作吧
    2023-06-06
  • golang 字符串切片去重實(shí)例

    golang 字符串切片去重實(shí)例

    這篇文章主要介紹了golang 字符串切片去重實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • 重學(xué)Go語言之?dāng)?shù)組的具體使用詳解

    重學(xué)Go語言之?dāng)?shù)組的具體使用詳解

    Go的數(shù)組是一種復(fù)合數(shù)據(jù)類型,在平時(shí)開發(fā)中并不常用,更常用的是切片(slice),可以把切片看作是能動態(tài)擴(kuò)容的數(shù)組,切片的底層數(shù)據(jù)結(jié)構(gòu)就是數(shù)組,所以數(shù)組雖不常用,但仍然有必要掌握
    2023-02-02
  • Go單例模式與Once源碼實(shí)現(xiàn)

    Go單例模式與Once源碼實(shí)現(xiàn)

    這篇文章主要介紹了Go單例模式與Once源碼實(shí)現(xiàn),本文結(jié)合示例代碼給大家講解的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-12-12
  • Go語言中strings和strconv包示例代碼詳解

    Go語言中strings和strconv包示例代碼詳解

    這篇文章主要介紹了Go語言中strings和strconv包示例代碼詳解 ,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-11-11

最新評論