Go 語言 unsafe.Sizeof, Alignof 和 Offsetof

2023-03-14 17:00 更新

原文鏈接:https://gopl-zh.github.io/ch13/ch13-01.html


13.1. unsafe.Sizeof, Alignof 和 Offsetof

unsafe.Sizeof函數(shù)返回操作數(shù)在內存中的字節(jié)大小,參數(shù)可以是任意類型的表達式,但是它并不會對表達式進行求值。一個Sizeof函數(shù)調用是一個對應uintptr類型的常量表達式,因此返回的結果可以用作數(shù)組類型的長度大小,或者用作計算其他的常量。

import "unsafe"
fmt.Println(unsafe.Sizeof(float64(0))) // "8"

Sizeof函數(shù)返回的大小只包括數(shù)據結構中固定的部分,例如字符串對應結構體中的指針和字符串長度部分,但是并不包含指針指向的字符串的內容。Go語言中非聚合類型通常有一個固定的大小,盡管在不同工具鏈下生成的實際大小可能會有所不同。考慮到可移植性,引用類型或包含引用類型的大小在32位平臺上是4個字節(jié),在64位平臺上是8個字節(jié)。

計算機在加載和保存數(shù)據時,如果內存地址合理地對齊的將會更有效率。例如2字節(jié)大小的int16類型的變量地址應該是偶數(shù),一個4字節(jié)大小的rune類型變量的地址應該是4的倍數(shù),一個8字節(jié)大小的float64、uint64或64-bit指針類型變量的地址應該是8字節(jié)對齊的。但是對于再大的地址對齊倍數(shù)則是不需要的,即使是complex128等較大的數(shù)據類型最多也只是8字節(jié)對齊。

由于地址對齊這個因素,一個聚合類型(結構體或數(shù)組)的大小至少是所有字段或元素大小的總和,或者更大因為可能存在內存空洞。內存空洞是編譯器自動添加的沒有被使用的內存空間,用于保證后面每個字段或元素的地址相對于結構或數(shù)組的開始地址能夠合理地對齊(譯注:內存空洞可能會存在一些隨機數(shù)據,可能會對用unsafe包直接操作內存的處理產生影響)。

類型 大小
bool 1個字節(jié)
intN, uintN, floatN, complexN N/8個字節(jié)(例如float64是8個字節(jié))
int, uint, uintptr 1個機器字
*T 1個機器字
string 2個機器字(data、len)
[]T 3個機器字(data、len、cap)
map 1個機器字
func 1個機器字
chan 1個機器字
interface 2個機器字(type、value)

Go語言的規(guī)范并沒有要求一個字段的聲明順序和內存中的順序是一致的,所以理論上一個編譯器可以隨意地重新排列每個字段的內存位置,雖然在寫作本書的時候編譯器還沒有這么做。下面的三個結構體雖然有著相同的字段,但是第一種寫法比另外的兩個需要多50%的內存。

                               // 64-bit  32-bit
struct{ bool; float64; int16 } // 3 words 4words
struct{ float64; int16; bool } // 2 words 3words
struct{ bool; int16; float64 } // 2 words 3words

關于內存地址對齊算法的細節(jié)超出了本書的范圍,也不是每一個結構體都需要擔心這個問題,不過有效的包裝可以使數(shù)據結構更加緊湊(譯注:未來的Go語言編譯器應該會默認優(yōu)化結構體的順序,當然應該也能夠指定具體的內存布局,相同討論請參考 Issue10014 ),內存使用率和性能都可能會受益。

unsafe.Alignof 函數(shù)返回對應參數(shù)的類型需要對齊的倍數(shù)。和 Sizeof 類似, Alignof 也是返回一個常量表達式,對應一個常量。通常情況下布爾和數(shù)字類型需要對齊到它們本身的大?。ㄗ疃?個字節(jié)),其它的類型對齊到機器字大小。

unsafe.Offsetof 函數(shù)的參數(shù)必須是一個字段 x.f,然后返回 f 字段相對于 x 起始地址的偏移量,包括可能的空洞。

圖 13.1 顯示了一個結構體變量 x 以及其在32位和64位機器上的典型的內存?;疑珔^(qū)域是空洞。

var x struct {
    a bool
    b int16
    c []int
}

下面顯示了對x和它的三個字段調用unsafe包相關函數(shù)的計算結果:


32位系統(tǒng):

Sizeof(x)   = 16  Alignof(x)   = 4
Sizeof(x.a) = 1   Alignof(x.a) = 1 Offsetof(x.a) = 0
Sizeof(x.b) = 2   Alignof(x.b) = 2 Offsetof(x.b) = 2
Sizeof(x.c) = 12  Alignof(x.c) = 4 Offsetof(x.c) = 4

64位系統(tǒng):

Sizeof(x)   = 32  Alignof(x)   = 8
Sizeof(x.a) = 1   Alignof(x.a) = 1 Offsetof(x.a) = 0
Sizeof(x.b) = 2   Alignof(x.b) = 2 Offsetof(x.b) = 2
Sizeof(x.c) = 24  Alignof(x.c) = 8 Offsetof(x.c) = 8

雖然這幾個函數(shù)在不安全的unsafe包,但是這幾個函數(shù)調用并不是真的不安全,特別在需要優(yōu)化內存空間時它們返回的結果對于理解原生的內存布局很有幫助。



以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號