iOS底層探究-05:內(nèi)存對(duì)齊

獲取內(nèi)存大小的三種方式

  • sizeof
  • class_getInstanceSize
  • malloc_size

sizeof

  • 1、sizeof是一個(gè)操作符,不是函數(shù)
  • 2、我們一般用sizeof計(jì)算內(nèi)存大小時(shí),傳入的是數(shù)據(jù)類型,這個(gè)在編譯器的編譯階段(即編譯時(shí))就會(huì)確定大小而不是在運(yùn)行時(shí)確定。
  • 3、sizeof最終得到的結(jié)果是該數(shù)據(jù)類型占用空間的大小

class_getInstanceSize

這個(gè)方法在iOS底層探究-03:alloc & init & new 源碼分析分析時(shí)就已經(jīng)分析了,是runtime提供的api,用于獲取類的實(shí)例對(duì)象所占用的內(nèi)存大小,并返回具體的字節(jié)數(shù),其本質(zhì)就是獲取實(shí)例對(duì)象中成員變量的內(nèi)存大小

malloc_size

這個(gè)函數(shù)是獲取系統(tǒng)實(shí)際分配的內(nèi)存大小

通過以下代碼驗(yàn)證一下:



以下是打印結(jié)果:


總結(jié)

  • sizeof:計(jì)算類型占用的內(nèi)存大小,其中可以放 基本數(shù)據(jù)類型、對(duì)象、指針

    • 對(duì)于類似于int這樣的基本數(shù)據(jù)而言,sizeof獲取的就是數(shù)據(jù)類型占用的內(nèi)存大小,不同的數(shù)據(jù)類型所占用的內(nèi)存大小是不一樣的

    • 而對(duì)于類似于NSObject定義的實(shí)例對(duì)象而言,其對(duì)象類型的本質(zhì)就是一個(gè)結(jié)構(gòu)體(即 struct objc_object)的指針,所以sizeof(objc)打印的是對(duì)象objc的指針大小,我們知道一個(gè)指針的內(nèi)存大小是8,所以sizeof(objc) 打印是 8。注意:這里的8字節(jié)與isa指針一點(diǎn)關(guān)系都沒有?。。。?/p>

    • 對(duì)于指針而言,sizeof打印的就是8,因?yàn)橐粋€(gè)指針的內(nèi)存大小是8

  • class_getInstanceSize:計(jì)算對(duì)象實(shí)際占用的內(nèi)存大小,這個(gè)需要依據(jù)類的屬性而變化,如果自定義類沒有自定義屬性,僅僅只是繼承自NSObject,則類的實(shí)例對(duì)象實(shí)際占用的內(nèi)存大小是8,可以簡單理解為8字節(jié)對(duì)齊
  • malloc_size:計(jì)算對(duì)象實(shí)際分配的內(nèi)存大小,這個(gè)是由系統(tǒng)完成的,可以從上面的打印結(jié)果看出,實(shí)際分配的和實(shí)際占用的內(nèi)存大小并不相等,這個(gè)問題可以通過iOS底層探究-03:alloc & init & new 源碼分析中的16字節(jié)對(duì)齊算法來解釋這個(gè)問題

結(jié)構(gòu)體內(nèi)存對(duì)齊

我們定義兩個(gè)結(jié)構(gòu)體,分別計(jì)算他們的內(nèi)存大小,以此來探究內(nèi)存對(duì)齊原理

struct Person {
    double a;   // 8
    char b;     // 1
    int c;      // 4
    short d;    // 2
} person;

struct Bus {
    double a;   // 8
    int b;      // 4
    char c;     // 1
    short d;    // 2
} bus;

NSLog(@"%lu-%lu",sizeof(person),sizeof(bus));

以下是輸出結(jié)果:


從打印結(jié)果我們可以看出一個(gè)問題,兩個(gè)結(jié)構(gòu)體乍一看,沒什么區(qū)別,其中定義的變量 和 變量類型都是一致的,唯一的區(qū)別只是在于定義變量的順序不一致,那為什么他們做占用的內(nèi)存大小不相等呢?其實(shí)這就是iOS中的內(nèi)存字節(jié)對(duì)齊現(xiàn)象

內(nèi)存對(duì)齊原則有以下三點(diǎn):
  • 1:數(shù)據(jù)成員對(duì)?規(guī)則:結(jié)構(gòu)(struct)或(聯(lián)合(union))的數(shù)據(jù)成員,第一個(gè)數(shù)據(jù)成員放在offset為0的地方,以后每個(gè)數(shù)據(jù)成員存儲(chǔ)的起始位置要從該成員大小或者成員的子成員大?。ㄖ灰摮蓡T有子成員,比如說是數(shù)組,結(jié)構(gòu)體等)的整數(shù)倍開始(比如int為4字節(jié)),則要從4的整數(shù)倍地址開始存儲(chǔ)。
  • 2:結(jié)構(gòu)體作為成員:如果一個(gè)結(jié)構(gòu)里有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲(chǔ)。(struct a里存有struct b,b里有char,int ,double等元素,那b應(yīng)該從8的整數(shù)倍開始存儲(chǔ))
  • 3:結(jié)構(gòu)體的總大小:也就是sizeof的結(jié)果,必須是其內(nèi)部最大成員的整數(shù)倍,不足的要補(bǔ)?。

驗(yàn)證對(duì)齊規(guī)則

下表是各種數(shù)據(jù)類型在ios中的占用內(nèi)存大小,根據(jù)對(duì)應(yīng)類型來計(jì)算結(jié)構(gòu)體中內(nèi)存大小


結(jié)構(gòu)體 Person 內(nèi)存大小計(jì)算

根據(jù)內(nèi)存對(duì)齊規(guī)則計(jì)算Person的內(nèi)存大小,詳解過程如下:

  • 變量a:占8個(gè)字節(jié),從0開始,此時(shí)min(0,8),即 0 -7 存儲(chǔ) a
  • 變量b:占1個(gè)字節(jié),從8開始,此時(shí)min(8,1),8可以整除1,即 8 存儲(chǔ) b
  • 變量c:占4個(gè)字節(jié),從9開始,此時(shí)min(9,4),9不能整除4,繼續(xù)往后移動(dòng),直到min(12,4),12可以整除4,從12開始,即 12 -15 存儲(chǔ) c
  • 變量d:占2個(gè)字節(jié),從16開始,此時(shí)min(16, 2),16可以整除2,即16 - 17 存儲(chǔ) d
    因此Person的需要的內(nèi)存大小為 18字節(jié),而Person最大變量的字節(jié)數(shù)為8,所以 Person 實(shí)際的內(nèi)存大小必須是 8 的整數(shù)倍,18向上取整到24,主要是因?yàn)?4是8的整數(shù)倍,所以 sizeof(Person) 的結(jié)果是 24
結(jié)構(gòu)體 Bus 內(nèi)存大小計(jì)算

根據(jù)內(nèi)存對(duì)齊規(guī)則計(jì)算Bus的內(nèi)存大小,詳解過程如下:

  • 變量a:占8個(gè)字節(jié),從0開始,此時(shí)min(0,8),即 0 -7 存儲(chǔ) a
  • 變量b:占4個(gè)字節(jié),從8開始,此時(shí)min(8,4),8可以整除4,即 8 -11 存儲(chǔ) b
  • 變量c:占1個(gè)字節(jié),從12開始,此時(shí)min(12,1),12可以整除1,即 12 存儲(chǔ) c
  • 變量d:占2個(gè)字節(jié),從13開始,此時(shí)min(13, 2),13不能整除2,繼續(xù)往后移動(dòng),直到min(14,2),14可以整除2,從14開始,即14 - 15 存儲(chǔ) d
    因此Bus的需要的內(nèi)存大小為 16字節(jié),而Bus最大變量的字節(jié)數(shù)為8,所以 Bus 實(shí)際的內(nèi)存大小必須是 8 的整數(shù)倍(就不需要向上取整),16剛好是8的整數(shù)倍,所以 sizeof(Bus) 的結(jié)果是 16
結(jié)構(gòu)體嵌套結(jié)構(gòu)體

在定義一個(gè)結(jié)構(gòu)體PersonBus,在PersonBus中嵌套Bus,如下所示

struct PersonBus {
    double a;   // 8
    char b;     // 1
    int c;      // 4
    short d;    // 2
    struct Bus bus1;
} personBus;

NSLog(@"%lu-%lu",sizeof(personBus),sizeof(personBus.bus1));

打印結(jié)果如下:



分析 PersonBus 的內(nèi)存計(jì)算

  • 變量a:占8個(gè)字節(jié),從0開始,此時(shí)min(0,8),即 0 -7 存儲(chǔ) a
  • 變量b:占1個(gè)字節(jié),從8開始,此時(shí)min(8,1),8可以整除1,即 8 存儲(chǔ) b
  • 變量c:占4個(gè)字節(jié),從9開始,此時(shí)min(9,4),9不能整除4,繼續(xù)往后移動(dòng),直到min(12,4),12可以整除4,從12開始,即 12 -15 存儲(chǔ) c
  • 變量d:占2個(gè)字節(jié),從16開始,此時(shí)min(16, 2),16可以整除2,即16 - 17 存儲(chǔ) d
  • 結(jié)構(gòu)體成員bus1bus1是一個(gè)結(jié)構(gòu)體,根據(jù)內(nèi)存對(duì)齊2:結(jié)構(gòu)體作為成員結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍開始存儲(chǔ),而Bus中最大的成員大小為8,所以bus1要從8的整數(shù)倍開始,當(dāng)前是從18開始,所以不符合要求,需要往后移動(dòng)到24,24是8的整數(shù)倍,符合內(nèi)存對(duì)齊原則,所以 24-39 存儲(chǔ) bus1
    因此PersonBus的需要的內(nèi)存大小為 40字節(jié),而PersonBus中最大變量為bus1, 其最大成員內(nèi)存字節(jié)數(shù)為8,根據(jù)內(nèi)存對(duì)齊原則,所以 PersonBus 實(shí)際的內(nèi)存大小必須是 8 的整數(shù)倍,40正好是8的整數(shù)倍,所以 sizeof(PersonBus) 的結(jié)果是 40
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容