unsafe,顧名思義,是不安全的,Go定義這個包名也是這個意思,讓咱們儘量的不要使用它,若是你使用它,看到了這個名字,也會想到儘量的不要使用它,或者更當心的使用它。express
雖然這個包不安全,可是它也有它的優點,那就是能夠繞過Go的內存安全機制,直接對內存進行讀寫,因此有時候由於性能的須要,會冒一些風險使用該包,對內存進行操做。安全
Sizeof
函數能夠返回一個類型所佔用的內存大小,這個大小隻有類型有關,和類型對應的變量存儲的內容大小無關,好比bool型佔用一個字節、int8也佔用一個字節。ide
對於整型來講,佔用的字節數意味着這個類型存儲數字範圍的大小,好比int8佔用一個字節,也就是8bit,因此它能夠存儲的大小範圍是-128~~127,也就是−2^(n-1)到2^(n-1)−1,n表示bit,int8表示8bit,int16表示16bit,其餘以此類推。函數
對於和平臺有關的int類型,這個要看平臺是32位仍是64位,會取最大的。好比我本身測試,以上輸出,會發現int和int64的大小是同樣的,由於個人是64位平臺的電腦。佈局
以上是Sizeof
的函數定義,它接收一個ArbitraryType
類型的參數,返回一個uintptr
類型的值。這裏的ArbitraryType
不用關心,他只是一個佔位符,爲了文檔的考慮導出了該類型,可是通常不會使用它,咱們只須要知道它表示任何類型,也就是咱們這個函數能夠接收任意類型的數據。性能
Alignof
返回一個類型的對齊值,也能夠叫作對齊係數或者對齊倍數。對齊值是一個和內存對齊有關的值,合理的內存對齊能夠提升內存讀寫的性能,關於內存對齊的知識能夠參考相關文檔,這裏不展開介紹。測試
從以上例子的輸出,能夠看到,對齊值通常是2^n,最大不會超過8(緣由見下面的內存對齊規則)。Alignof
的函數定義和Sizeof
基本上同樣。這裏須要注意的是每一個人的電腦運行的結果可能不同,大同小異。ui
此外,獲取對齊值還可使用反射包的函數,也就是說:unsafe.Alignof(x)
等價於reflect.TypeOf(x).Align()
。spa
Offsetof
函數只適用於struct結構體中的字段相對於結構體的內存位置偏移量。結構體的第一個字段的偏移量都是0.code
字段的偏移量,就是該字段在struct結構體內存佈局中的起始位置(內存位置索引從0開始)。根據字段的偏移量,咱們能夠定位結構體的字段,進而能夠讀寫該結構體的字段,哪怕他們是私有的,***的感受有沒有。偏移量的概念,咱們會在下一小結詳細介紹。
此外,unsafe.Offsetof(u1.i)
等價於reflect.TypeOf(u1).Field(i).Offset
咱們定義一個struct,這個struct有3個字段,它們的類型有byte
,int32
以及int64
,可是這三個字段的順序咱們能夠任意排列,那麼根據順序的不一樣,一共有6種組合。
根據這6種組合,定義了6個struct,分別位user1,user2,…,user6,那麼如今你們猜想一下,這6種類型的struct佔用的內存是多少,就是unsafe.Sizeof()
的值。
你們可能猜想1+4+8=13,由於byte的大小爲1,int32大小爲4,int64大小爲8,而struct其實就是一個字段的組合,因此猜想struct大小爲字段大小之和也很正常。
可是,可是,我能夠明確的說,這是錯誤的。
爲何是錯誤的,由於有內存對齊存在,編譯器使用了內存對齊,那麼最後的大小結果就不同了。如今咱們正式驗證下,這幾種struct的值。
從以上輸出能夠看到,結果是:
結果出來了(個人電腦的結果,Mac64位,你的可能不同),4個16字節,2個24字節,既不同,又不相同,這說明:
內存對齊影響struct的大小
struct的字段順序影響struct的大小
綜合以上兩點,咱們能夠得知,不一樣的字段順序,最終決定struct的內存大小,因此有時候合理的字段順序能夠減小內存的開銷。
內存對齊會影響struct的內存佔用大小,如今咱們就詳細分析下,爲何字段定義的順序不一樣會致使struct的內存佔用不同。
在分析以前,咱們先看下內存對齊的規則:
對於具體類型來講,對齊值=min(編譯器默認對齊值,類型大小Sizeof長度)。也就是在默認設置的對齊值和類型的內存佔用大小之間,取最小值爲該類型的對齊值。個人電腦默認是8,因此最大值不會超過8.
struct在每一個字段都內存對齊以後,其自己也要進行對齊,對齊值=min(默認對齊值,字段最大類型長度)。這條也很好理解,struct的全部字段中,最大的那個類型的長度以及默認對齊值之間,取最小的那個。
以上這兩條規則要好好理解,理解明白了才能夠分析下面的struct結構體。在這裏再次提醒,對齊值也叫對齊係數、對齊倍數,對齊模數。這就是說,每一個字段在內存中的偏移量是對齊值的倍數便可。
咱們知道byte,int32,int64的對齊值分別爲1,4,8,佔用內存大小也是1,4,8。那麼對於第一個structuser1
,它的字段順序是byte、int3二、int64,咱們先使用第1條內存對齊規則進行內存對齊,其內存結構以下,內存佈局中有豎線(|),用於每四個字節的分割,下同。
user1
類型,第1個字段byte,對齊值1,大小1,因此放在內存佈局中的第1位。
第2個字段int32,對齊值4,大小4,因此它的內存偏移值必須是4的倍數,在當前的user1
中,就不能從第2位開始了,必須從第5位開始,也就是偏移量爲4。第2,3,4位由編譯器進行填充,通常爲值0,也稱之爲內存空洞。因此第5位到第8位爲第2個字段i。
第3字段,對齊值爲8,大小也是8。由於user1
前兩個字段已經排到了第8位,因此下一位的偏移量正好是8,是第3個字段對齊值的倍數,不用填充,能夠直接排列第3個字段,也就是從第9位到第16位爲第3個字段j。
如今第一條內存對齊規則後,內存長度已經爲16個字節,咱們開始使用內存的第2條規則進行對齊。根據第二條規則,默認對齊值8,字段中最大類型長度也是8,因此求出結構體的對齊值位8,咱們目前的內存長度爲16,是8的倍數,已經實現了對齊。
因此到此爲止,結構體user1
的內存佔用大小爲16字節。
如今咱們再分析一個user2
類型,它的大小是24,只是調換了一下字段i和j的順序,就多佔用了8個字節,咱們看看爲何?仍是先使用咱們的內存第1條規則分析。
按對齊值和其佔用的大小,第1個字段b偏移量爲0,佔用1個字節,放在第1位。
第2個字段j,是int64,對齊值和大小都是8,因此要從偏移量8開始,也就是第9到16位爲j,這也就意味着第2到8位被編譯器填充。
目前整個內存佈局已經偏移了16位,正好是第3個字段i的對齊值4的倍數,因此不用填充,能夠直接排列,第17到20位爲i。
如今全部字段對齊好了,整個內存大小爲1+7+8+4=20個字節,咱們開始使用內存對齊的第2條規則,也就是結構體的對齊,經過默認對齊值和最大的字段大小,求出結構體的對齊值爲8。
如今咱們的整個內存佈局大小爲20,不是8的倍數,因此咱們須要進行內存填充,補足到8的倍數,最小的就是24,因此對齊後整個內存佈局爲
因此這也是爲何咱們最終得到的user2
的大小爲24的緣由。
基於以上辦法,咱們能夠得出其餘幾個struct的內存佈局。
user3
user4
user5
user6
以上給出了答案,推到過程你們能夠參考user1
和user2
試試。