如何得到一個對象所占內存大小?
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 [3]int32 } unsafe.Sizeof(SizeOfD{}) // 16 unsafe.Alignof(SizeOfD{}) // 4
Alignof返回的對齊數是結構體中單位基本類型所占的內存數,不超過8,如果元素是數組那么取數組元素類型所占的內存值而不是整個數組的值。
如圖為SizeofD結構體變量的內存布局:
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字節。
如圖為SizeofE結構體變量的內存布局:
type SizeOfF struct { A byte // 1 C byte // 1 B int64 // 8 } unsafe.Sizeof(SizeOfF{}) // 16 unsafe.Alignof(SizeOfF{}) // 8
換一種寫法,把A,C放到上面,B放到下面。這時SizeOfE占用的內存變為了16字節。因為首先分配的8字節足以放下A和C,省去了8字節的空間。
上面一個結構體中元素的不同順序足以導致內存分配的巨大差異。前一種寫法產生了很多的內存空洞,導致結構體不夠緊湊,造成內存浪費。如圖為SizeofF結構體變量的內存布局:
下面我們來看一下結構體中元素的內存布局:
unsafe.Offsetof:返回結構體中元素所在內存的偏移量
type SizeOfH struct { A byte C int16 B int64 D int32 } unsafe.Offsetof(SizeOfH{}.A) // 0 unsafe.Offsetof(SizeOfH{}.C) // 2 unsafe.Offsetof(SizeOfH{}.B) // 8 unsafe.Offsetof(SizeOfH{}.D) // 16
下圖為SizeOfH 內存布局圖:

藍色區域是元素實際所占內存,灰色為內存空洞。
下面總結一下go語言中各種類型所占內存大小(x64環境下):
X64下1機器字節=8字節

總結一下:
從例子中可以看出,結構體中元素不同順序的排列會導致內存分配的極大差異,不好的順序會產生許多的內存空洞,造成大量內存浪費。
雖然這幾個函數都在unsafe包中,但是他們並不是不安全的。在需要優化內存空間時這幾個函數非常有用。