引言
內(nèi)存對齊
是內(nèi)存里面一個很重要的詞匯傅瞻,可是大部分開發(fā)者對這個詞匯的含義都是一知半解拧粪。
WJPerson*wj = [WJPerson alloc];
wj.name = @"無極";
wj.age = 30;
NSLog(@"對象類型的內(nèi)存大小:%lu",sizeof(wj));
NSLog(@"對象實際的內(nèi)存大小:%lu",class_getInstanceSize([wj class]));
NSLog(@"對象分配的內(nèi)存大小:%lu",malloc_size((__bridge const void *)(wj)));
NSLog(@"-----------------------------------------");
WJPerson*wj2;
NSLog(@"對象類型的內(nèi)存大邪锓恰:%lu",sizeof(wj2));
NSLog(@"對象實際的內(nèi)存大小:%lu",class_getInstanceSize([wj2 class]));
NSLog(@"對象分配的內(nèi)存大卸锬ⅰ:%lu",malloc_size((__bridge const void *)(wj2)));
輸出結(jié)果:
2021-06-16 13:12:08.712404+0800 內(nèi)存對齊[3440:72350] 對象類型的內(nèi)存大心┛:8
2021-06-16 13:12:08.712537+0800 內(nèi)存對齊[3440:72350] 對象實際的內(nèi)存大小:24
2021-06-16 13:12:08.712659+0800 內(nèi)存對齊[3440:72350] 對象分配的內(nèi)存大凶俊:32
2021-06-16 13:12:08.712746+0800 內(nèi)存對齊[3440:72350] -----------------------------------------
2021-06-16 13:12:08.712831+0800 內(nèi)存對齊 [3440:72350] 對象類型的內(nèi)存大性刹铡:8
2021-06-16 13:12:08.712923+0800 內(nèi)存對齊[3440:72350] 對象實際的內(nèi)存大小:0
2021-06-16 13:12:08.713007+0800 內(nèi)存對齊[3440:72350] 對象分配的內(nèi)存大邪孀小:0
結(jié)果分析:
-
sizeof
:對象類型的內(nèi)存大小隅忿,sizeof
是用來計算一個變量
或者一個常量
心剥、一種數(shù)據(jù)類型
所占的內(nèi)存字節(jié)數(shù)
。自定義對象的本質(zhì)是結(jié)構(gòu)體指針
背桐,所以占8
個字節(jié)。 -
class_getInstanceSize
:對象實際(對齊后)的內(nèi)存大小蝉揍,內(nèi)存大小是由類的成員變量
的大小決定的链峭。實際上并不是嚴格意義上的對象的內(nèi)存的大小,因為內(nèi)存進行了8
字節(jié)對齊又沾,所以wj
的內(nèi)存大小是24
而不是20
弊仪。而wj2只是聲明變量,并沒有走alloc
方法開辟內(nèi)存杖刷,所以大小是0
励饵。核心內(nèi)存大小算法是:define WORD_MASK 7UL ((x + WORD_MASK) & ~WORD_MASK
-
malloc_size
:系統(tǒng)實際分配
的內(nèi)存大小,以16
字節(jié)對齊滑燃,不足16
的自動補齊役听。注意:
系統(tǒng)的16字節(jié)對齊是在實際的內(nèi)存大小(經(jīng)過8
字節(jié)對齊后)的基礎(chǔ)上表窘。上面的wj對象實際內(nèi)存大小24
字節(jié)典予,不是16
的倍數(shù),所以系統(tǒng)實際分配為32
乐严。
問題:
class_getInstanceSize
和malloc_size
底層做了什么瘤袖?我們?nèi)绾沃?code>class_getInstanceSize是8
字節(jié)對齊,而malloc_size
是16
字節(jié)對齊昂验?
在研究后面重點之前捂敌,我們先來看下基本數(shù)據(jù)類型在arm64
環(huán)境下占用的內(nèi)存大小。
下面解釋為什么計算機會有內(nèi)存對齊
的概念既琴,出于什么目的
要內(nèi)存對齊占婉。
- 內(nèi)存是以
字節(jié)
為基本單位,cpu
在讀取數(shù)據(jù)時呛梆,是以塊
為單位讀取锐涯,并不是以字節(jié)
為單位讀取。頻繁
讀取未對齊的數(shù)據(jù)填物,會加大cpu
的開銷纹腌。字節(jié)對齊后,會降低cpu
的存取次數(shù)滞磺,這種以空間
換時間
的做法降低了cpu
的開銷升薯。 -
cpu
存取:是以塊
為單位击困,存取未對齊
的數(shù)據(jù)可能開始在上一個內(nèi)存塊涎劈,結(jié)束在另一個內(nèi)存塊广凸。這樣中間可能要經(jīng)過復(fù)雜的運算在合并在一起,降低了效率蛛枚,字節(jié)對齊后谅海,提高了cpu
的訪問效率。
內(nèi)存對齊規(guī)則:
數(shù)據(jù)成員對齊規(guī)則:
結(jié)構(gòu)體(struct)(或聯(lián)合體(union))
的數(shù)據(jù)成員蹦浦,第一個數(shù)據(jù)成員放在offset為0的地方(即首地址的位置)扭吁,以后每個數(shù)據(jù)成員存儲的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員盲镶,比如說是數(shù)組侥袜,結(jié)構(gòu)體等)的整數(shù)倍
開始(比如int
為4
字節(jié)),則要從4的整數(shù)倍
地址開始存儲溉贿。
結(jié)構(gòu)體
作為成員變量
:如果一個結(jié)構(gòu)體
里有某些結(jié)構(gòu)體成員
枫吧,則結(jié)構(gòu)體成員
要從其內(nèi)部最大元素大小的整數(shù)倍
開始存儲(struct a
里有struct b
,b里有char宇色,int九杂,double等元素,那b
應(yīng)該從8(doudle為8 )
的整數(shù)倍
開始存儲)
下面我們先來看一個例子:
struct WJPerson1{
double a;
char b;
int c;
short d;
}myPerson1;
struct WJPerson2
{
double b;
int c;
char a;
short d;
}myPerson2;
NSLog(@"%lu-%lu",sizeof(myPerson1),sizeof(myPerson2));
輸出結(jié)果:
2021-06-16 16:13:21.621334+0800 內(nèi)存對齊[4074:158189] 24-16
從上面我們可以看出代兵,myPerson1
和myPerson2
兩個結(jié)構(gòu)體
里面元素是一樣的尼酿,只是順序不同
,內(nèi)存大小卻不一樣
植影,為什么裳擎?這就是結(jié)構(gòu)體內(nèi)存對齊。
具體分析如下:[p,q]
p
表示當前開始
的位置思币,q
表示大小
myPerson1:
- double a:
[0,7]
即(0~7
存放a) - char b:
[8,1]
即(8
存放b) - int c:
[12,15]
即(9,10,11不是int 4得出倍數(shù)鹿响,廢棄,12~15
存放c) - short d:
[16,2]
即(16 ~ 17
存放d)
myPerson2:
- double a:
(0,7)
即(0~7
存放a) - int b:
(8,4)
即(8~11
存放b) - char c:
(12,1)
即(12
存放c) - short d:
(14,2)
即(13不是d( short 2的倍數(shù))位置廢棄谷饿,14 ~ 15
存放d)
下面這個是嵌套的結(jié)構(gòu)體
struct WJPerson3 {
double a;
int b;
char c;
short d;
int e;
struct WJPerson1 str;
} myPerson3;
myPerson3具體分析如下:
- double a:
[0,7]
即(0~7
存放a) - char b:
[8,1]
即(8
存放b) - int c:
[12,15]
即(9,10,11不是int 4得出倍數(shù)惶我,廢棄,12~15
存放c) - short d:
[16,2]
即(16 ~ 17
存放d) - int e:
(20,4)
即(18,19不是int 4得出倍數(shù)博投,廢棄.20 ~ 23
存放d) - 變量str: str是結(jié)構(gòu)體變量绸贡,內(nèi)存對齊原則
結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲
。WJPerson1 中的最大的變量a( double)占8字節(jié)毅哗,所以offset從24
開始听怕,WJPerson1的內(nèi)存大小是18
字節(jié)。[24虑绵,18]尿瞭,即24 ~ 42存放 str,計算出來的是42
個字節(jié)翅睛,但是myPerson3中最大
的變量是str
和 a都是8
字節(jié)声搁,所以myPerson3的實際內(nèi)存
大小必須是8
的整數(shù)倍黑竞,42
不是8
的整數(shù)倍,因此補齊應(yīng)該是48
.