美文网首页
内存字节对齐

内存字节对齐

作者: Easting | 来源:发表于2019-12-17 21:57 被阅读0次

    我们可以先看看下面的结构体,观察一下结构体的内存分配情况:

    struct Struct1 {
        int a;
        double b;
        int c;
        char d;
        short e;
    }myStruct1;
    
    struct Struct2 {
        int a;
        double b;
        char d;
        int c;
        short e;
    }myStruct2;
    
    NSLog(@"%lu - %lu - %lu",sizeof(myStruct1),sizeof(myStruct2));
    打印结果 24 - 32
    
    struct Struct3 {
        double b;
        char d;
        int c;
        short e;
        struct  Struct2 myStruct2;
    }myStruct3;
    NSLog(@"%lu",sizeof(myStruct3));
    打印结果 56
    
    struct Struct4 {
        int c;
    }myStruct4;
    
    struct Struct5 {
        double b;
        char d;
        int c;
        short e;
        struct  Struct4 myStruct4;
    }myStruct5;
    NSLog(@"%lu",sizeof(myStruct5));
    打印结果 24
    

    比较Struct1 和 Struct2 属性是相同的,但是属性的位置有一定的区别。这就是内存对齐的现象。
    比较Struct3 和 Struct5的区别,Struct2的属性里面的最大是8,Struct4里面最大是4

    内存对齐的规则

    每个特定的平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。我们可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是要指定的“对齐系数”。Xcode的对齐系数就是8。
    我们在了解内存对齐之前,先看看内存对齐的原则:

    1. 数据成员对齐规则:(Struct 或 union 的数据成员)第一个数据成员放在偏移为0的位置,以后每个成员的偏移为 min(对齐系数,自身长度)的整数倍,不够整数倍的补齐。
    2. 数据成员为结构体:该数据成员的自身长度为其最大长度的整数倍开始存储
    3. 整体对齐规则:数据成员按照上述规则对齐之后,其本身也要对齐,
      对齐原则是min(对其系数,成员最大长度)的整数倍。

    通过这个规则我们对Struct5做一下简单的描述
    double b 8字节 存储区间 [0-7];
    char d 1字节 存储区间[8-9];
    int c 4字节 还有[10-15]存储段 保持4的倍数 存储区间[11-16];
    short e 2字节 存储区间[17-18];
    接下来 struct Struct4 myStruct4;
    int c 4字节 有此仅有一个 需要的4的倍数的位置 存储区间[19-23];
    保证整体对齐规则 min(对其系数,成员最大长度)为8的整数倍 即24字节

    内存对齐原因

    内存对齐是编译器帮我们处理的。但一个程序要求CPU读取未对齐的数据时,CPU会进入异常处理状态并且通知程序不能继续执行。因为未对齐的数据,会大大的降低CPU的性能。
    CPU并不是以字节为单位来存取数据的,它会把内存当成一块一块的来,块的大小可以是2、4、8、16、32字节,每次读取都是一个固定的开销,减少内存存取次数将提升程序的性能。
    我们可以假设 CPU先从地址0 读取4字节到寄存器,这时候读取的是对齐地址的数据,但从地址1读取的时候是非对齐的数据,就可能需要读取几次才能完成,然后在合成之后到寄存器。

    OC内存对齐

    @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)));
    }
    x/6gx p1打印结果
    (lldb) x/6xg p1
    0x600000ce0000: 0x00000001029570d0 0x0000001200006261
    0x600000ce0010: 0x0000000102956098 0x00000000000000b4
    0x600000ce0020: 0x00000001029560b8 0x0000000000000000
    (lldb) po 0x00000001029570d0
    XDPerson
    (lldb) po 0x00000012
    18
    (lldb) po 0x62
    98
    (lldb) po 0x61
    97
    (lldb) po 0x0000000102956098
    xiedong
    (lldb) po 0x00000000000000b4
    180
    (lldb) po 0x00000001029560b8
    男
    
    NSLog打印的结果 40-48
    
    1. 可以猜到objc帮我们的对象属性做了优化处理,把size大小比较的小的 int char 组合在了一起.
    2. 然后会发现对象申请的内存空间<=系统开辟的内存空间.
      通过alloc的流程 我们会在objc源码里面看对齐方式是以8字节对齐的。
      通过calloc的流程 我们可以在malloc的源码的segregated_size_to_fit()函数里面可以看到对齐方式是以16字节对齐的。

    我们可以发现 对象内部的属性有自己的内存空间是保证安全的,那么对象与对象之间是怎么保证安全的呢?

    XDPerson对象内部的属性+isa占用的内存空间是28+8=36, 实际申请的内存是40 。
    那么多余申请的4字节是否可以保证对象与对象之间的安全(或者说这多余的4字节是两个对象地址之间空出来的那一段呢)
    结果并不是的,这多余的4字节可能是在这个对象内存空间的某个位置,并不一定在最后面。
    所以系统为了保证更加的安全,以16字节对齐的方式开辟对象的内存空间,保证对象的内存空间更大,对象与对象之间更加安全。

    相关文章

      网友评论

          本文标题:内存字节对齐

          本文链接:https://www.haomeiwen.com/subject/zvyenctx.html