-
研究內(nèi)存對齊原理之前缔赠,我們先要熟悉下表:
類型對應(yīng)表.jpg
知道對應(yīng)的內(nèi)存大小了涨醋,接下來我們需要獲取內(nèi)存,驗證是否正確郁季。
驗證內(nèi)存的三種方式:
1冷溃、sizeof
2钱磅、class_getInstanceSize
3、malloc_size
sizeof
- 1似枕、sizeof是一個操作符盖淡,不是函數(shù)
- 2、sizeof計算的是對象數(shù)據(jù)類型的大小凿歼,這個大小在編譯時確定的而不是運行時
- 3褪迟、sizeof最終得到的數(shù)據(jù)是該數(shù)據(jù)類型占用空間的大小,當sizeof(結(jié)構(gòu)體)的時候答憔,獲取到的是對象指針大小味赃,我們知道一個指針的內(nèi)存大小是8,所以sizeof獲取內(nèi)存大小時可以放基本數(shù)據(jù)類型虐拓、對象心俗、指針、
calss_getInstanceSize
這個方法是runtime提供的api蓉驹,用于獲取類的實例對象所占用的內(nèi)存大小另凌,并返回具體的字節(jié)數(shù),其本質(zhì)就是獲取實例對象中成員變量的內(nèi)存大小
malloc_size
這個函數(shù)是 獲取系統(tǒng)實際分配的內(nèi)存大小
通過下面的函數(shù)打印結(jié)果驗證上面的說法:
#import <Foundation/Foundation.h>
#import "LGPerson.h"
#import <objc/runtime.h>
#import <malloc/malloc.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *objc = [[NSObject alloc] init];
NSLog(@"objc對象類型占用的內(nèi)存大薪溽!:%lu",sizeof(objc));
NSLog(@"objc對象實際占用的內(nèi)存大蟹托弧:%lu",class_getInstanceSize([objc class]));
NSLog(@"objc對象實際分配的內(nèi)存大小:%lu",malloc_size((__bridge const void*)(objc)));
}
return 0;
}
打印結(jié)果如下:
從打印的結(jié)果看實際分配的內(nèi)存和實際內(nèi)存大小并不相等诗茎,不相等的原因是因為內(nèi)存16字節(jié)對齊的原則導(dǎo)致的工坊,內(nèi)存字節(jié)對齊的原理主要有以下三點:
1、數(shù)據(jù)成員對齊規(guī)則
:struct 或者 union 的數(shù)據(jù)成員敢订,第一個數(shù)據(jù)成員放在offset=0
的地方王污,以后每個數(shù)據(jù)成員存儲的其實位置要從該成員大小或者成員子成員大小(只要該成員有子成員比如結(jié)構(gòu)體)的整數(shù)倍開始(例如int在32位機中占4字節(jié)楚午,則要從4的整數(shù)倍地址開始存儲)
2昭齐、數(shù)據(jù)成員位結(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的整數(shù)倍開始存儲)
3里覆、結(jié)構(gòu)體的整體對齊規(guī)則
:結(jié)構(gòu)體的總大小,即 sizeof
的結(jié)果必須是其內(nèi)部最大成員的整數(shù)倍缆瓣,不足的要補齊
為什么是16位對齊
1喧枷、通常內(nèi)存是一個個字節(jié)組成的,cpu在存取數(shù)據(jù)時,并不是以字節(jié)為單位存儲隧甚,而是以塊為單位存取车荔,塊的大小為內(nèi)存存取力度,頻繁存取字節(jié)未對齊的數(shù)據(jù)戚扳,會極大降低CPU的性能夸赫,所以可以通過減少存取次數(shù)來降低cup的開銷
2、16字節(jié)對齊是因為一個對象中的第一個屬性isa
占8字節(jié)
咖城,當然對象中肯定還有其他屬性茬腿,當無屬性時會預(yù)留8字節(jié),即16字節(jié)對齊宜雀,如果不預(yù)留相當于這個對象的isa
和其它對象的isa
緊挨著切平,容易造成訪問混亂
3、16字節(jié)對齊后辐董,可以加快cpu的存取速度悴品,同時增加訪問安全性
16字節(jié)對齊的算法
- 首先將原始的內(nèi)存 8 與 size_t(15)相加,得到 8 + 15 = 23
- 將 size_t(15) 即 15進行(取反)操作简烘,(取反)的規(guī)則是:1變?yōu)?苔严,0變?yōu)?
-
最后將 23 與 15的取反結(jié)果 進行 &(與)操作,&(與)的規(guī)則是:都是1為1孤澎,反之為0届氢,最后的結(jié)果為 16,即內(nèi)存的大小是以16的倍數(shù)增加的
內(nèi)存16字節(jié)對齊算法.png
結(jié)構(gòu)體內(nèi)存對齊
接下來我們定義兩個結(jié)構(gòu)體分別計算他們的大懈残瘛:
//1退子、定義兩個結(jié)構(gòu)體
struct Mystruct1{
char a; //1字節(jié)
double b; //8字節(jié)
int c; //4字節(jié)
short d; //2字節(jié)
}Mystruct1;
struct Mystruct2{
double b; //8字節(jié)
int c; //4字節(jié)
short d; //2字節(jié)
char a; //1字節(jié)
}Mystruct2;
//計算 結(jié)構(gòu)體占用的內(nèi)存大小
NSLog(@"%lu-%lu",sizeof(Mystruct1),sizeof(Mystruct2));
輸出結(jié)果為:
a0000000bbbbbbbbccccdd =24
bbbbbbbbccccdda0=16 //結(jié)構(gòu)體的大小(sizeof)必須是最大成員的整數(shù)倍型将,所以15位需要補一位最后結(jié)果是16
兩個結(jié)構(gòu)體乍一看寂祥,沒什么區(qū)別,其中定義的變量 和 變量類型都是一致的七兜,唯一的區(qū)別只是在于定義變量的順序不一致丸凭,那為什么他們做占用的內(nèi)存大小不相等呢?其實這就是iOS中的內(nèi)存字節(jié)對齊現(xiàn)象
可以將內(nèi)存對齊原則可以理解為以下兩點:
*【原則一】 數(shù)據(jù)成員的對齊規(guī)則可以理解為min(m, n)
的公式, 其中 m
表示當前成員的開始位置, n
表示當前成員所需要的位數(shù)腕铸。如果滿足條件 m
整除 n
(即m % n == 0
), n
從 m
位置開始存儲, 反之繼續(xù)檢查 m+1
能否整除 n
, 直到可以整除, 從而就確定了當前成員的開始位置惜犀。
*【原則二】數(shù)據(jù)成員為結(jié)構(gòu)體:當結(jié)構(gòu)體嵌套了結(jié)構(gòu)體時,作為數(shù)據(jù)成員的結(jié)構(gòu)體的自身長度作為外部結(jié)構(gòu)體的最大成員的內(nèi)存大小恬惯,比如結(jié)構(gòu)體a
嵌套結(jié)構(gòu)體b
向拆,b
中有char
、int
酪耳、double
等,則b
的自身長度為8
*【原則三】最后結(jié)構(gòu)體的內(nèi)存大小必須是結(jié)構(gòu)體中最大成員內(nèi)存大小的整數(shù)倍,不足的需要補齊碗暗。