美文网首页
OC源码探索03:内存对齐原理

OC源码探索03:内存对齐原理

作者: 木扬音 | 来源:发表于2020-09-11 17:16 被阅读0次

    什么是内存对齐

    计算机中内存空间是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是:在访问特定类型变量的时候通常在特定的内存地址访问,这就需要对这些数据在内存中存放的位置有限制,各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。

    内存对齐是编译器的管辖范围。表现为:编译器为程序中的每个“数据单元”安排在适当的位置上。

    内存对齐规则

    每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。在ios中,Xcode默认为#pragma pack(8),即8字节对齐

    • 数据成员的对齐规则:m表示当前成员的开始位置, n表示当前成员所需要的位数。如果 m 整除 n , 当前成员从m位置开始存储n位, 反之继续检查 m+1 能否整除 n, 直到可以整除, 当前成员从m+1位置开始存储n位。
      9 10 11 12
    • 数据成员为结构体:如果⼀个结构体⾥包含其他结构体成员,则这个结构体成员要从其内部最⼤元素⼤⼩的整数倍地址开始存储.(struct a⾥存有struct b,b⾥有char,int ,double等元素,那b应该从8的整数倍开始存储.)
    • 结构体的⼤⼩,必须是其内部最⼤成员的整数倍.不⾜的要补⻬。

    各类数据在iOS中占用的内存大小

    数据内存大小表

    验证内存对齐规则

    首先我们定义两个结构体来分析对应的内存大小

    struct YPStruct1 {
        double a;    // 8
        char b;     // 1
        int c;      // 4
        short d;     // 2
    }YPStruct1;
    
    struct YPStruct2 {
        double a;   //8
        int b;      //4
        char c;     //1
        short d;    //2
    }YPStruct2;
    
    struct YPStruct3 {
        double a;   //8
        int b;      //4
        char c;     //1
        short d;    //2
        int e;     //4
        struct YPStruct2 struct2; //16
    }YPStruct3;
    
    NSLog(@"YPStruct1 == %lu , YPStruct2 == %lu , YPStruct3 == %lu , YPStruct3.struct2 == %lu",
                  sizeof(YPStruct1),sizeof(YPStruct2),sizeof(YPStruct3),sizeof(YPStruct3.struct2));
    

    内存打印结果


    内存打印结果

    YPStruct1内存计算

    YPStruct1内存计算
    • 变量a:从第0位开始,占8字节 所以存储在[0-7]
    • 变量b:从第8位开始,占1字节 所以存储在[8-8]
    • 变量c:从第12位开始,占4字节 因为9不能整除4,所以初始位向后移,所以存储在[12-15]
    • 变量d:从第16位开始,占2字节 所以存储在[16-17]

    因此YPStruct1的需要的内存大小为17字节,而YPStruct1中最大变量的字节数为8,所以 YPStruct1 实际的内存大小必须是 8 的整数倍,17向上取整到24,主要是因为24是8的整数倍,所以sizeof(YPStruct1) 的结果是 24

    YPStruct2内存计算

    YPStruct2内存计算
    • 变量a:从第0位开始,占8字节 所以存储在[0-7]
    • 变量b:从第8位开始,占4字节 所以存储在[8-11]
    • 变量c:从第12位开始,占1字节 所以存储在[12-12]
    • 变量d:从第13位开始,占2字节 所以存储在[13-15]

    因此YPStruct2的需要的内存大小为15字节,而YPStruct2中最大变量的字节数为8,所以 YPStruct2 实际的内存大小必须是 8 的整数倍,15向上取整到16,主要是因为24是8的整数倍,所以sizeof(YPStruct2) 的结果是 16

    YPStruct3内存计算

    YPStruct3内存计算
    • 变量a:从第0位开始,占8字节 所以存储在[0-7]
    • 变量b:从第8位开始,占4字节 所以存储在[8-11]
    • 变量c:从第12位开始,占1字节 所以存储在[12-12]
    • 变量d:从第13位开始,占2字节 所以存储在[13-14]
    • 变量e:从第16位开始,占4字节 所以存储在[16-19],因为15不能整除4,所以初始位向后移
    • 结构体struct2:因为struct2是一个结构体,结构体成员要从其内部最大成员大小的整数倍开始存储struct2的最大成员字节为8,由于当前是从19开始的,不是8的整数倍,所以要向后移动到24,从24位开始,占16字节 所以存储在[24-39],因为39不是8的整数倍,所以向后移动到40,所以sizeof(YPStruct3) 的结果是 40

    内存优化(属性重排)

    • 自定义一个YPPerson类,声明几个属性
    @interface YPPerson : NSObject
    
    @property (nonatomic, copy) NSString *name;
    @property (nonatomic, copy) NSString *nickName;
    // @property (nonatomic, copy) NSString *hobby;
    @property (nonatomic, assign) int age;
    @property (nonatomic, assign) long height;
    
    @property (nonatomic) char c1;
    @property (nonatomic) char c2;
    
    @end
    
    • 在main中创建YPPerson的实例对象,并对其中的几个属性赋值
    int main(int argc, char * argv[]) {
        NSString * appDelegateClassName;
        @autoreleasepool {
            YPPerson *person = [YPPerson alloc];
            person.name      = @"Cooci";
            person.nickName  = @"KC";
            person.age       = 18;
            person.c1        = 'a';
            person.c2        = 'b';
    
            NSLog(@"%@",person);
            appDelegateClassName = NSStringFromClass([AppDelegate class]);
        }
        return UIApplicationMain(argc, argv, nil, appDelegateClassName);
    }
    
    • 通过断点调试,根据YPPerson对象地址,查找对应的属性值
      image.png
      • 0x00000001044e4028地址代表name的值
      • 0x00000001044e4048地址代表nickName的值
      • 但是当我们打印0x0000001200006261地址时,发现是乱码,是因为苹果对age、c1、c2属性进行了内存重排,因为age类型占4个字节,c1和c2类型char分别占1个字节,通过4+1+1的方式,按照8字节对齐,不足补齐的方式存储在同一块内存中
        • 0x00000012地址代表age的值
        • 0x62地址代表c1的值,ASCII码的形式
        • 0x61地址代表c2的值,ASCII码的形式
      • 0x0000000000000000地址代表没有赋值的属性

    总结

    • YPStruct1 通过内存字节对齐原则,增加了9个字节,而YPStruct2通过内存字节对齐原则,通过4+2+1的组合,只需要补齐一个字节即可满足字节对齐规则,这里我们可以知道结构体内存大小与结构体成员内存大小的顺序有关
    • 我们在内存中采用了内存对齐的方式,但并不是所有的内存都可以进行浪费的,苹果会自动对属性进行重排,以此来优化内存

    相关文章

      网友评论

          本文标题:OC源码探索03:内存对齐原理

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