iOS 底層探索篇 —— 內存字節對齊分析

前言程序員

1、內存對齊規則

1.對齊係數

每一個特定的平臺上的編譯器都有本身的默認「對齊係數」(也叫對齊模數)。咱們能夠經過預編譯命令#pragma pack(n),n=一、二、四、八、16 來改變這一系數,其中的n就是要指定的「對齊係數」。咱們iOS編譯器Xcode的對齊係數就是8。安全

2.對齊規則

  1. 數據成員對齊規則:(Struct或者Union的數據成員)第一個數據成員放在偏移爲0的位置。之後每一個數據成員的位置爲min(對齊係數,自身長度)的整數倍,下個位置不爲本數據成員的整數倍位置的自動補齊。
  2. 數據成員爲結構體:該數據成員的內最大長度的整數倍的位置開始存儲。
  3. 總體對齊規則:數據成員按照1,2步驟對齊以後,其自身也要對齊,對齊原則是min(對齊係數,數據成員最大長度)的整數倍。

2、結構體內存分析

1.不用變量的內存分析

struct Struct1 {
    double a;
    int b;
    char c;
    short d;
}myStruct1;

struct Struct2 {
    int a;
    double b;
    int c;
    char d;
}myStruct2;

NSLog(@"myStruct1 - %lu",sizeof(myStruct1));
NSLog(@"myStruct2 - %lu",sizeof(myStruct2));
複製代碼
  • 對於一些基本的數據類型所佔用的字節大小,你們應該都是很是清楚的了。
  • 經過打印輸出的結果能夠看到myStruct1 - 16myStruct2 - 24.

分析:bash

Struct1類型 位置 補齊 Struct2類型 位置 補齊
double a [0 - 7] 0 int a [0 - 3] 4
int b [8 - 11] 0 double b [8 - 15] 0
char c [12 - 12] 1 int c [16 - 19] 0
short b [14 - 15] 0 char b [20 - 20]
  • Struct1總體對齊以後:大小爲16。
  • Struct2總體對齊以後:大小爲24。

2.相同變量的內存分析

struct Struct1 {
    double a;
    int b;
    char c;
    short d;
}myStruct1;

struct Struct2 {
    int a;
    double b;
    char d;
    short e;
}myStruct2;

NSLog(@"myStruct1 - %lu",sizeof(myStruct1));
NSLog(@"myStruct2 - %lu",sizeof(myStruct2));
複製代碼
  • 經過打印輸出的結果能夠看到myStruct1 - 16myStruct2 - 24.

分析:post

Struct1類型 位置 補齊 Struct2類型 位置 補齊
double a [0 - 7] 0 int a [0 - 3] 4
int b [8 - 11] 0 double b [8 - 15] 0
char c [12 - 12] 1 char c [16 - 16] 1
short b [14 - 15] 0 short d [18 - 19]
  • Struct1總體對齊以後:大小爲16。
  • Struct2總體對齊以後:大小爲24。

3.結構體做爲變量的內存分析

struct Struct1 {
    double a;
    int b;
    char c;
    short d;
}myStruct1;

struct Struct2 {
    int a;
    double b;
    char d;
    struct Struct1 myStruct1;
}myStruct2;
NSLog(@"myStruct2 - %lu",sizeof(myStruct2));
複製代碼
  • 經過打印輸出的結果能夠看到myStruct2 - 24.

分析:性能

Struct2 類型 位置 補齊
int a [0 - 3] 4
double b [8 - 15] 0
char c [16 - 16] 7

咱們按照規則來算,成員爲結構體的,按照結構體的本身內部數據成員的最大長度的整數倍儲存。atom

Struct2 類型 位置 補齊
double a [24 - 31] 0
int b [32 - 35] 0
char c [36 - 36] 1
short d [38 - 39]
  • Struct2總體對齊以後:大小爲40。

3、OC類屬性內存分析

1.自定義類和屬性

@interface XDPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) long height;
@property (nonatomic, copy) NSString *sex;
@property (nonatomic) char ch1;
@property (nonatomic) char ch2;

@end

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
   
    XDPerson *p1 = [XDPerson alloc];
    p1.name = @"xiedong";
    p1.age = 18;
    p1.height = 180;
    p1.sex = @"男";
    p1.ch1 = 'a';
    p1.ch2 = 'b';
    
   NSLog(@"%lu - %lu",class_getInstanceSize([p1 class]),malloc_size((__bridge const void *)(p1)));
}
複製代碼

輸出結果 40 - 48。spa

  • 對象申請的內存空間 <= 系統開闢的內存空間。
  • 對象申請的內存空間是以8字節對齊方式。在objc源碼裏面是能夠獲得驗證的。
  • 系統開闢內存空間是以16字節對齊方式。在malloc源碼裏面segregated_size_to_fit()能夠看到是以16字節對齊的。

2.lldb調試查看

x/6xg p1意思表明 讀取p1對象6段內存地址。調試

(lldb) x/6xg p1
0x600000ce0000: 0x00000001029570d0 0x0000001200006261
0x600000ce0010: 0x0000000102956098 0x00000000000000b4
0x600000ce0020: 0x00000001029560b8 0x0000000000000000
(lldb) po 0x00000001029570d0 & 0x0000000ffffffff8
XDPerson
(lldb) po 0x00000012
18
(lldb) po 0x62
98
(lldb) po 0x61
97
(lldb) po 0x0000000102956098
xiedong
(lldb) po 0x00000000000000b4
180
(lldb) po 0x00000001029560b8
男
複製代碼

發現OC裏面程序員寫的屬性的順序並非內存裏面的順序,與結構體struct仍是有必定的區別。其實這裏就是編譯器給進行二進制重排產生的效果。code

  • 第一個內存地址是isa,是objc_object這個基類帶的數據成員。後面的章節中會有所介紹。

4、內存對齊緣由

  1. 內存對齊是編譯器處理的。
  2. CPU讀取未對齊的內存時,其性能會大大的下降,此時CPU會進入到異常狀態,而且通知程序不能繼續進行。
  3. CPU並非以字節爲單位來存取數據的,它會把內存當成一塊一塊的,其塊的大小能夠是二、四、八、1六、32字節,每次讀取都是一個固定的開銷,減小內存存取次數提高應用程序的性能。

咱們能夠想一下,假設CPU先從0地址讀取4字節到寄存器,這個時候內存是對齊的,一次讀取4字節。而後在從1地址讀取,先讀取2字節,再讀取2字節,而後再合成到寄存器,這個時候CPU的性能就會相對上一次下降,對整個應用程序的性能一定會產生相應的影響。對象

5、內存對齊在OC中的優勢

有時候咱們會思考爲何系統開闢的內存大小會大於咱們申請的內存大小呢?按照8字節對齊的方式,申請的內存就可能已經存在多餘的了,就拿上面的例子int和兩個char就會多了兩字節。

  1. 按照8字節對齊方式,對象內部裏面的成員內存地址是絕對安全的。
  2. 咱們沒法肯定申請多餘的字節就在對象與對象之間,有可能會出如今對象內存段的內部某個位置,這個時候就可能會出現兩個對象內存段是挨着的狀況,沒有那麼的安全。系統開闢空間採起16字節方式,保證對象的內存空間會更大,對象與對象之間更加的安全。
相關文章
相關標籤/搜索