引言
內(nèi)存對齊是內(nèi)存里面一個(gè)很重要的詞匯,可是大部分開發(fā)者對這個(gè)詞匯的含義都是一知半解。
WJPerson*wj = [WJPerson alloc];
wj.name = @"無極";
wj.age = 30;
NSLog(@"對象類型的內(nèi)存大?。?lu",sizeof(wj));
NSLog(@"對象實(shí)際的內(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(@"對象實(shí)際的內(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)存大?。?
2021-06-16 13:12:08.712537+0800 內(nèi)存對齊[3440:72350] 對象實(shí)際的內(nèi)存大小:24
2021-06-16 13:12:08.712659+0800 內(nèi)存對齊[3440:72350] 對象分配的內(nèi)存大?。?2
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)存大?。?
2021-06-16 13:12:08.712923+0800 內(nèi)存對齊[3440:72350] 對象實(shí)際的內(nèi)存大小:0
2021-06-16 13:12:08.713007+0800 內(nèi)存對齊[3440:72350] 對象分配的內(nèi)存大?。?
結(jié)果分析:
-
sizeof:對象類型的內(nèi)存大小,sizeof是用來計(jì)算一個(gè)變量或者一個(gè)常量、一種數(shù)據(jù)類型所占的內(nèi)存字節(jié)數(shù)。自定義對象的本質(zhì)是結(jié)構(gòu)體指針,所以占8個(gè)字節(jié)。 -
class_getInstanceSize:對象實(shí)際(對齊后)的內(nèi)存大小,內(nèi)存大小是由類的成員變量的大小決定的。實(shí)際上并不是嚴(yán)格意義上的對象的內(nèi)存的大小,因?yàn)閮?nèi)存進(jìn)行了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)實(shí)際分配的內(nèi)存大小,以16字節(jié)對齊,不足16的自動補(bǔ)齊。注意:系統(tǒng)的16字節(jié)對齊是在實(shí)際的內(nèi)存大小(經(jīng)過8字節(jié)對齊后)的基礎(chǔ)上。上面的wj對象實(shí)際內(nèi)存大小24字節(jié),不是16的倍數(shù),所以系統(tǒng)實(shí)際分配為32。
問題:
class_getInstanceSize和malloc_size底層做了什么?我們?nèi)绾沃?code>class_getInstanceSize是8字節(jié)對齊,而malloc_size是16字節(jié)對齊?
在研究后面重點(diǎn)之前,我們先來看下基本數(shù)據(jù)類型在arm64環(huán)境下占用的內(nèi)存大小。

下面解釋為什么計(jì)算機(jī)會有內(nèi)存對齊的概念,出于什么目的要內(nèi)存對齊。
- 內(nèi)存是以
字節(jié)為基本單位,cpu在讀取數(shù)據(jù)時(shí),是以塊為單位讀取,并不是以字節(jié)為單位讀取。頻繁讀取未對齊的數(shù)據(jù),會加大cpu的開銷。字節(jié)對齊后,會降低cpu的存取次數(shù),這種以空間換時(shí)間的做法降低了cpu的開銷。 -
cpu存?。菏且?code>塊為單位,存取未對齊的數(shù)據(jù)可能開始在上一個(gè)內(nèi)存塊,結(jié)束在另一個(gè)內(nèi)存塊。這樣中間可能要經(jīng)過復(fù)雜的運(yùn)算在合并在一起,降低了效率,字節(jié)對齊后,提高了cpu的訪問效率。
內(nèi)存對齊規(guī)則:
數(shù)據(jù)成員對齊規(guī)則:
結(jié)構(gòu)體(struct)(或聯(lián)合體(union))的數(shù)據(jù)成員,第一個(gè)數(shù)據(jù)成員放在offset為0的地方(即首地址的位置),以后每個(gè)數(shù)據(jù)成員存儲的起始位置要從該成員大小或者成員的子成員大?。ㄖ灰摮蓡T有子成員,比如說是數(shù)組,結(jié)構(gòu)體等)的整數(shù)倍開始(比如int為4字節(jié)),則要從4的整數(shù)倍地址開始存儲。
結(jié)構(gòu)體作為成員變量:如果一個(gè)結(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ù)倍開始存儲)
下面我們先來看一個(gè)例子:

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兩個(gè)結(jié)構(gòu)體里面元素是一樣的,只是順序不同,內(nèi)存大小卻不一樣,為什么?這就是結(jié)構(gòu)體內(nèi)存對齊。
具體分析如下:[p,q] p表示當(dāng)前開始的位置,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)
下面這個(gè)是嵌套的結(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,計(jì)算出來的是42個(gè)字節(jié),但是myPerson3中最大的變量是str和 a都是8字節(jié),所以myPerson3的實(shí)際內(nèi)存大小必須是8的整數(shù)倍,42不是8的整數(shù)倍,因此補(bǔ)齊應(yīng)該是48.