如何得到一個對象所占內存大小?
fmt.Println(unsafe.Sizeof(int64(0))) // "8"
type SizeOfA struct {
A int
}
unsafe.Sizeof(SizeOfA{0}) // 8
type SizeOfC struct {
A byte // 1字節
C int32 // 4字節
}
unsafe.Sizeof(SizeOfC{0, 0}) // 8
unsafe.Alignof(SizeOfC{0, 0}) // 4
結構體中A byte占1字節,C int32占4字節. SizeOfC占8字節
內存對齊:
為何會有內存對齊?1.并不是所有硬件平臺都能訪問任意地址上的任意數據。2.性能原因 訪問未對齊的內存,處理器需要做兩次內存訪問,而對齊的內存只需訪問一次。
上面代碼SizeOfC中元素一共5個字節,而實際結構體占8字節
是因為這個結構體的對齊倍數Alignof(SizeOfC) = 4.也就是說,結構體占的實際大小必須是4的倍數,也就是8字節。
type SizeOfD struct {
A byte
B [5]int32
}
unsafe.Sizeof(SizeOfD{}) // 24
unsafe.Alignof(SizeOfD{}) // 4
Alignof返回的對齊數是結構體中最大元素所占的內存數,不超過8,如果元素是數組那么取數組類型所占的內存值而不是整個數組的值
type SizeOfE struct {
A byte // 1
B int64 // 8
C byte // 1
}
unsafe.Sizeof(SizeOfE{}) // 24
unsafe.Alignof(SizeOfE{}) // 8
SizeOfE中,元素的大小分別為1,8,1,但是實際結構體占24字節,遠超元素實際大小,因為內存對齊原因,最開始分配的8字節中包含了1字節的A,剩余的7字節不足以放下B,又為B分配了8字節,剩余的C獨占再分配的8字節。
type SizeOfE struct {
A byte // 1
C byte // 1
B int64 // 8
}
unsafe.Sizeof(SizeOfE{}) // 16
unsafe.Alignof(SizeOfE{}) // 8
換一種寫法,把A,C放到上面,B放到下面。這時SizeOfE占用的內存變為了16字節。因為首先分配的8字節足以放下A和C,省去了8字節的空間。
上面一個結構體中元素的不同順序足以導致內存分配的巨大差異。前一種寫法產生了很多的內存空洞,導致結構體不夠緊湊,造成內存浪費。
下面我們來看一下結構體中元素的內存布局:
unsafe.Offsetof:返回結構體中元素所在內存的偏移量
type SizeOfF struct {
A byte
C int16
B int64
D int32
}
unsafe.Offsetof(SizeOfF{}.A) // 0
unsafe.Offsetof(SizeOfF{}.C) // 2
unsafe.Offsetof(SizeOfF{}.B) // 8
unsafe.Offsetof(SizeOfF{}.D) // 16
下圖為內存分布圖:
SizeOfF 內存布局圖
藍色區域是元素實際所占內存,灰色為內存空洞。
下面總結一下go語言中各種類型所占內存大小(x64環境下):
X64下1機器字節=8字節
Golang內置類型占用內存大小
總結一下:
從例子中可以看出,結構體中元素不同順序的排列會導致內存分配的極大差異,不好的順序會產生許多的內存空洞,造成大量內存浪費。
雖然這幾個函數都在unsafe包中,但是他們并不是不安全的。在需要優化內存空間時這幾個函數非常有用。