> 文中所涉及到的代碼運行結果均是在64位機器上執行得到的. ## 基礎知識回顧 在Go中,我們可以使用`unsafe.Sizeof(x)`來查看變數所占的記憶體大小。以下是Go內置的數據類型占用的記憶體大小: | 類型 | 記憶體大小(位元組數) | | : | : | | bool | 1 | | in ...
文中所涉及到的代碼運行結果均是在64位機器上執行得到的.
基礎知識回顧
在Go中,我們可以使用unsafe.Sizeof(x)
來查看變數所占的記憶體大小。以下是Go內置的數據類型占用的記憶體大小:
類型 | 記憶體大小(位元組數) |
---|---|
bool | 1 |
int8/uint8 | 1 |
int/uint | 8 |
int32/uint32 | 4 |
int64/uint64 | 8 |
float32 | 4 |
float64 | 8 |
complex64 | 8 |
complex128 | 16 |
指針類型:*T, map,func,chan | 8 |
string | 16 |
interface | 16 |
[]T | 24 |
func main() {
fmt.Println(unsafe.Sizeof(true)) // 1
fmt.Println(unsafe.Sizeof(int8(1))) // 1
fmt.Println(unsafe.Sizeof(int(1))) // 8
fmt.Println(unsafe.Sizeof(int32(1))) // 4
fmt.Println(unsafe.Sizeof(int64(1))) // 8
fmt.Println(unsafe.Sizeof(float32(1.0))) // 4
fmt.Println(unsafe.Sizeof(float64(1.0))) // 8
a := int(1)
fmt.Println(unsafe.Sizeof(&a)) // 8
s := "1234"
fmt.Println(unsafe.Sizeof(s)) //16
var b interface{}
fmt.Println(unsafe.Sizeof(b)) //16
fmt.Println(unsafe.Sizeof([]string{})) // 24
fmt.Println(unsafe.Sizeof([]int{})) // 24
}
簡單示例
對於一個結構體,其占用的記憶體大小應該是其內部多個基礎類型占用記憶體大小之和。但實際情況並非如此,甚至欄位順序不同,結構體的大小也不同:
type Example1 struct {
a int32 // 4
b int32 // 4
c int64 // 8
}
type Example2 struct {
a int32 // 4
c int64 // 8
b int32 // 4
}
type Example3 struct {
a bool // 1
b int8 // 1
c string // 16
}
func main() {
fmt.Println(unsafe.Sizeof(Example1{})) // 16
fmt.Println(unsafe.Sizeof(Example2{})) // 24
fmt.Println(unsafe.Sizeof(Example3{})) // 24,並不是1+1+16=18
}
為什麼會出現上面的情況呢?這就引出了本文的重點:記憶體對齊。
記憶體對齊
記憶體對齊(Memory Alignment)是指數據在電腦記憶體中存儲時按照特定規則對齊到記憶體地址的過程。記憶體對齊是由電腦硬體和操作系統所決定的,它可以提高記憶體訪問效率和系統性能。
在電腦體繫結構中,記憶體是以位元組(byte)為單位進行訪問的。數據類型在記憶體中占用的位元組數可以是不同的,例如,整數可能占用2位元組、4位元組或8位元組,而字元可能只占用1位元組。
記憶體對齊的規則要求變數的地址必須是其數據類型位元組數的整數倍。例如,如果一個變數的數據類型是4位元組(32位),那麼它的起始地址必須是4的倍數。
記憶體對齊的主要目的是優化電腦的記憶體訪問性能。當數據按照對齊要求存儲在記憶體中時,讀取和寫入操作可以更高效地進行。如果數據沒有按照對齊要求存儲,電腦可能需要進行多次記憶體讀取操作來獲取完整的數據,這會增加訪問延遲和降低系統性能。
在編程中,特別是在使用結構體和類的語言中,記憶體對齊是一個重要的概念。編譯器會根據數據類型的對齊要求自動進行記憶體對齊操作,以確保數據存儲的正確性和性能優化。但在某些情況下,可以通過顯式地設置對齊屬性來控制數據的對齊方式,以滿足特定的需求。
需要註意的是,不同的硬體平臺和操作系統可能具有不同的記憶體對齊規則和要求。因此,在開發跨平臺應用程式時,應當考慮到這些差異並遵循適當的記憶體對齊規則。
為什麼需要對齊記憶體
記憶體對齊是為了提高電腦系統的記憶體訪問效率和性能而存在的。以下是幾個需要記憶體對齊的原因:
- 硬體要求:許多電腦硬體和體繫結構對記憶體訪問有特定的對齊要求。如果數據沒有按照硬體要求進行對齊,可能會導致訪問錯誤、異常或性能下降。通過滿足硬體對齊要求,可以確保數據能夠按照有效的方式訪問,提高系統的穩定性和性能。
- 記憶體訪問效率:當數據按照對齊要求存儲在記憶體中時,電腦系統可以更高效地訪問這些數據。對齊數據可以減少或避免多次記憶體訪問,提高數據的讀取和寫入速度。這對於大量的數據操作和高性能計算非常重要。
- 緩存性能:現代電腦系統中通常有多級緩存,而緩存的訪問是以特定塊的方式進行的。記憶體對齊可以確保數據按照緩存塊的大小對齊,使得數據能夠更好地利用緩存,減少緩存未命中和讀取延遲,提高緩存性能。
- 結構體和類的記憶體佈局:結構體和類通常包含多個成員變數,這些變數按照一定的順序存儲在記憶體中。記憶體對齊確保結構體和類的成員變數按照正確的順序和對齊要求進行存儲,避免記憶體空洞和訪問錯誤,保證數據的正確性和一致性。
總之,記憶體對齊是為了滿足硬體要求、提高記憶體訪問效率和性能而引入的機制。通過合理地進行記憶體對齊,可以提高系統的穩定性、性能和響應速度,並避免潛在的記憶體訪問問題。
如何對齊記憶體
Go團隊開發了一款名為fieldalignment
的工具可以幫助我們解決記憶體對齊的問題。
使用下麵的命令安裝fieldalignment
工具:
$ go install golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@latest
還是以上面的代碼為例,可以執行下麵的命令:
$ fieldalignment main.go
main.go:14:15: struct of size 24 could be 16
main.go:20:15: struct with 16 pointer bytes could be 8
也可以使用--fix
參數直接修改代碼:
$ fieldalignment --fix main.go
修改後的內容如下:
type Example1 struct {
a int32 // 4
b int32 // 4
c int64 // 8
}
type Example2 struct {
c int64
a int32
b int32
}
type Example3 struct {
c string
a bool
b int8
}
![孟斯特](https://img2023.cnblogs.com/blog/1007709/202305/1007709-20230511101217207-642908395.jpg)
聲明:本作品採用署名-非商業性使用-相同方式共用 4.0 國際 (CC BY-NC-SA 4.0)進行許可,使用時請註明出處。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 戀水無意