獲取內(nèi)存大小方式
1.sizeof
2.class_getInstanceSize
3.malloc_size
sizeof
1.sizeof
是一個(gè)操作符號(hào),不是函數(shù);
2.我們一般用sizeof
計(jì)算內(nèi)存大小時(shí),傳入的主要對(duì)象是數(shù)據(jù)類型,這個(gè)在編譯器的編譯階段(即編譯時(shí))就會(huì)確定大小而不是在運(yùn)行時(shí)確定,對(duì)于NSObject
這樣的類而言,其定義的數(shù)據(jù)類型本質(zhì)就是一個(gè)結(jié)構(gòu)體的指針谬运,而指針的內(nèi)存大小是8字節(jié)
,所以對(duì)象類型占用的字節(jié)數(shù)為8字節(jié)垦藏。
3.sizeof
最終得到的結(jié)果是該數(shù)據(jù)類型占用空間的大小;
class_getInstanceSize
這個(gè)方式是runtime提供的api梆暖,用于獲取類的實(shí)例對(duì)象占用的內(nèi)存空間大小,返回結(jié)果為具體字節(jié)數(shù)掂骏,本質(zhì)是獲取實(shí)例對(duì)象中成員變量占用的內(nèi)存空間轰驳。如果自定義類沒(méi)有定義屬性,僅僅只是繼承自NSObject弟灼,則這個(gè)實(shí)例對(duì)象實(shí)際占用的大小為8字節(jié)级解,使用的為8字節(jié)對(duì)齊算法
。
malloc_size
這個(gè)函數(shù)是獲取系統(tǒng)實(shí)際分配的內(nèi)存大小田绑,使用的為16字節(jié)對(duì)齊的算法
勤哗。
可以將內(nèi)存對(duì)齊原則可以理解為以下幾點(diǎn):
【原則一
】 數(shù)據(jù)成員的對(duì)齊規(guī)則可以理解為min(m, n) 的公式, 其中 m表示當(dāng)前成員的開(kāi)始位置, n表示當(dāng)前成員所需要的位數(shù)。如果滿足條件 m 整除 n (即 m % n == 0), n 從 m 位置開(kāi)始存儲(chǔ), 反之繼續(xù)檢查 m+1 能否整除 n, 直到可以整除, 從而就確定了當(dāng)前成員的開(kāi)始位置掩驱。
【原則二
】數(shù)據(jù)成員為結(jié)構(gòu)體:當(dāng)結(jié)構(gòu)體嵌套了結(jié)構(gòu)體時(shí)芒划,作為數(shù)據(jù)成員的結(jié)構(gòu)體的自身長(zhǎng)度作為外部結(jié)構(gòu)體的最大成員的內(nèi)存大小冬竟,比如結(jié)構(gòu)體a嵌套結(jié)構(gòu)體b,b中有char民逼、int泵殴、double等,則b的自身長(zhǎng)度為8
【原則三
】最后結(jié)構(gòu)體的內(nèi)存大小必須是結(jié)構(gòu)體中最大成員內(nèi)存大小的整數(shù)倍缴挖,不足的需要補(bǔ)齊袋狞。
結(jié)構(gòu)體內(nèi)存對(duì)齊
內(nèi)存對(duì)齊可以理解為結(jié)構(gòu)體中的成員申請(qǐng)內(nèi)存大小時(shí)焚辅,系統(tǒng)最小分配的內(nèi)存為8字節(jié)映屋,是按每次8字節(jié)來(lái)申請(qǐng)的,不夠8字節(jié)的也會(huì)申請(qǐng)8字節(jié)同蜻,然后按結(jié)構(gòu)體中成員變量順序再次申請(qǐng)棚点,直到所有成員變量都能放下為止。
舉例來(lái)說(shuō)明
//1湾蔓、定義兩個(gè)結(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;
//計(jì)算 結(jié)構(gòu)體占用的內(nèi)存大小
NSLog(@"%lu-%lu",sizeof(Mystruct1),sizeof(Mystruct2));
Mystruct1
和Mystruct2
成員一樣瘫析,只是順序不同,最后的結(jié)果是內(nèi)存大小不一樣默责。sizeof(Mystruct1) 為24,sizeof(Mystruct2))為16;
贬循,如下圖所示:
Mystruct1
詳解過(guò)程:
1.Mystruct1 中每次申請(qǐng)內(nèi)存按字節(jié)最長(zhǎng)的變量來(lái)申請(qǐng),最長(zhǎng)的變量b占8個(gè)字節(jié)桃序, 變量a占一個(gè)字節(jié)杖虾,申請(qǐng)只能按8字節(jié)申請(qǐng),實(shí)際只使用了一個(gè)字節(jié)媒熊;
2.變量b占8個(gè)字節(jié)奇适,如果順序存放則會(huì)被分割到2個(gè)字節(jié)中,一個(gè)存放7個(gè)另一個(gè)存放1個(gè)芦鳍,這樣的話取值時(shí)就會(huì)錯(cuò)亂嚷往,為了按正確的偏移位置取到正確的值,系統(tǒng)按每次8個(gè)字節(jié)的偏移量來(lái)讀取內(nèi)存柠衅。這樣的結(jié)果就是就算a只用了一個(gè)字節(jié)還有7個(gè)未用皮仁,接下來(lái)的b占8字節(jié)也要重新分配至少能存放下b的新內(nèi)存,再次申請(qǐng)一個(gè)8字節(jié)內(nèi)存菲宴;
3.變量c占4個(gè)字節(jié)贷祈,存放在第三次申請(qǐng)的8字節(jié)的內(nèi)存,還有4字節(jié)沒(méi)有用到裙顽,變量d占2字節(jié)付燥,可以存放在c所在的8字節(jié)內(nèi)存中,所有申請(qǐng)的內(nèi)存加起來(lái)為24個(gè)字節(jié)大小愈犹,雖然實(shí)際只使用了15字節(jié)键科。
Mystruct2
詳解過(guò)程:
1.Mystruct2 中每次申請(qǐng)內(nèi)存按字節(jié)最長(zhǎng)的變量來(lái)申請(qǐng)闻丑,變量b占8個(gè)字節(jié),第一次申請(qǐng)的內(nèi)存全部用來(lái)存放b勋颖;
2.變量c占4字節(jié)嗦嗡,第二次申請(qǐng)的內(nèi)存存放c后還剩4個(gè)字節(jié)沒(méi)用用到,d占2字節(jié)饭玲,可以存放侥祭,還剩2字節(jié),變量a占一個(gè)字節(jié)茄厘,也可以存放矮冬,這樣存放后,還剩一個(gè)字節(jié)空余次哈,所有申請(qǐng)的內(nèi)存加起來(lái)為16胎署,雖然實(shí)際只用了15個(gè)字節(jié)。
嵌套結(jié)構(gòu)體內(nèi)存對(duì)齊
嵌套結(jié)構(gòu)體內(nèi)存對(duì)齊詳解:
//1窑滞、結(jié)構(gòu)體嵌套結(jié)構(gòu)體
struct Mystruct3{
double b; //8字節(jié)
int c; //4字節(jié)
short d; //2字節(jié)
char a; //1字節(jié)
struct Mystruct2 str;
}Mystruct3;
//2琼牧、打印 Mystruct3 的內(nèi)存大小
NSLog(@"Mystruct3內(nèi)存大小:%lu", sizeof(Mystruct3));
NSLog(@"Mystruct3中結(jié)構(gòu)體成員內(nèi)存大邪馈:%lu", sizeof(Mystruct3.str));
//先說(shuō)結(jié)果Mystruct3內(nèi)存大芯薹弧:32,
//Mystruct3中結(jié)構(gòu)體成員內(nèi)存大写烁摹:16
分析Mystruct3
計(jì)算過(guò)程如下:
1.結(jié)構(gòu)體成員Mystruct2 str
是一個(gè)結(jié)構(gòu)體趾撵,根據(jù)內(nèi)存對(duì)齊原則二,Mystruct2
中最大的成員大小為8带斑,b,c,d,a最大字節(jié)為8鼓寺,所以每次申請(qǐng)按8字節(jié)大小來(lái);
2.b,c,d,a內(nèi)存分析和Mystruct2 str
一樣勋磕,內(nèi)存大小為16妈候,加上str類型也為Mystruct2 str
,也是16挂滓,總共大小為32苦银;
內(nèi)存優(yōu)化(屬性重排)
從上面的分析可知,結(jié)構(gòu)體內(nèi)存大小與結(jié)構(gòu)體成員的順序有關(guān)
.
當(dāng)結(jié)構(gòu)體成員內(nèi)存小的在前面時(shí)會(huì)因?yàn)閮?nèi)存對(duì)齊的原因比較浪費(fèi)內(nèi)存赶站,為了解決這個(gè)問(wèn)題蘋果中采用空間換時(shí)間幔虏,將類中的屬性進(jìn)行重排,以此來(lái)達(dá)到內(nèi)存優(yōu)化的目的
總結(jié)
1.大部分的內(nèi)存都是通過(guò)固定的內(nèi)存快進(jìn)行讀缺创弧想括;
2.盡管我們?cè)趦?nèi)存中采取了內(nèi)存對(duì)齊的方式,但是并不是所有的內(nèi)存都是可以進(jìn)行浪費(fèi)的烙博,蘋果會(huì)自動(dòng)進(jìn)行屬性重排以此來(lái)優(yōu)化內(nèi)存瑟蜈;
class_getInstanceSize
:采用8字節(jié)對(duì)齊
烟逊,參照的對(duì)象的屬性內(nèi)存大小铺根;
malloc_size
:采用16字節(jié)對(duì)齊
宪躯,參照整個(gè)對(duì)象的內(nèi)存大小,對(duì)象實(shí)際分配的內(nèi)存大小必須是16的整數(shù)倍位迂。