美文网首页专注iOS开发的小渣渣
OC底层探索05-内存对齐

OC底层探索05-内存对齐

作者: Henry________ | 来源:发表于2020-09-12 19:04 被阅读0次
上篇对对象实际内存占用和内存分配计算做了解释,但是留了一个小坑:对象属性的大小计算。

众所周知对象最终是以结构体的形式存在,所以通过对结构体的大小的探索来延伸到对象.

内存对齐

普通结构体,例1:

struct HRStruct {
    int c;  //4
    double a;   //8
    char b; //1
    char d;    //1
}HRStruct;

NSLog(@"HRStruct结构体大小: %lu",sizeof(HRStruct));
  • 直接计算: 4 + 8 + 1 + 1 = 14

以下是输出结果:


为什么会这样呢,带着问题我们继续。

普通结构体,例2

struct HRStruct {
    double a;   //8
    int c;  //4
    char b; //1
    char d;    //1
}HRStruct;
  • 直接计算: 8 + 4 + 1 + 1 = 14

仔细观察这个结构体中的变量是相同的,不同的只是顺序.
以下是输出结果:


得出一个结论:根据顺序不同也会造成所占内存大小不同,可是为什么会这样呢?我们继续。

结构体嵌套,例3

struct HRStruct {
    double a;   //8
    int c;  //4
    char b; //1
    char d;    //1
}HRStruct;

struct HRStruct2 {
    double a;   //8
    int c;  //4
    char b; //1
    struct HRStruct str;    // 16
}HRStruct2;
  • 直接计算: 8 + 4 + 1 + 16 = 29

以下是输出结果:


内存的计算并不是简单的加法,而是做了一定的调整.

内存对齐原则

内存的计算是按照以下规则:

  • 【原则一】 数据成员对⻬规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要
    从该成员大小或者成员的子成员大小的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储
  • 【原则二】收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍.不足的要补⻬。
  • 【原则三】结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b
    里有char,int ,double等元素,那b应该从8的整数倍开始存储.),结构体作为成员也需要按最大成员的整数倍进行补⻬。

例子验证

各种类型大小,方便计算时查看
例1:
struct HRStruct {
    int c;  //4
    double a;   //8
    char b; //1
    char d;    //1
}HRStruct;
  • 通过内存布局图,直观的查看内存占用情况


例2:
struct HRStruct {
    double a;   //8
    int c;  //4
    char b; //1
    char d;    //1
}HRStruct;
  • 通过内存布局图,直观的查看内存占用情况


例3:
struct HRStruct {
    double a;   //8
    int c;  //4
    char b; //1
    char d;    //1
}HRStruct;

struct HRStruct2 {
    double a;   //8
    int c;  //4
    char b; //1
    struct HRStruct str;    // 16
}HRStruct2;
  • 通过内存布局图,直观的查看内存占用情况!


内存优化(属性重排)

通过例1、例2的对比,我们得出一个结论:根据顺序不同也会造成所占内存大小不同。所以通过调整的成员顺序可以优化内存大小、优化内存的布局,从而提高内存的读取效率.

  • 如果是结构体中数据成员是根据内存从小到大的顺序定义的,根据内存对齐规则来计算结构体内存大小,需要增加有较大的内存padding即内存占位符,才能满足内存对齐规则,比较浪费内存
  • 如果是结构体中数据成员是根据内存从大到小的顺序定义的,根据内存对齐规则来计算结构体内存大小,我们只需要补齐少量内存padding即可满足堆存对齐规则,这种方式就是苹果中采用的,利用空间换时间,将类中的属性进行重排,来达到优化内存的目的

我们能想到这个点,苹果的开发工程师一定也想到了这个点。

我们通过一个对象的内存情况来验证一下我们的猜测:

@interface HRTest : NSObject
        @property(nonatomic, copy)NSString *name;
        @property(nonatomic, copy)NSString *hobby;
        @property(nonatomic, assign)double height;
        @property(nonatomic, assign)char a;
        @property(nonatomic, assign)char ab;
@end

        HRTest * t = [HRTest alloc];
        t.name = @"Henry";
        t.hobby = @"woman";
        t.height = 180.0;
        t.a = 'a';
        t.ab = 'A';

打印结果:


  • name、hobby是变量的第一个、第两个属性,为什么却存在第三、第四个指针中呢?
第一个指针:isa指针
第二个指针:短类型重排

直接打印是乱码,但是仔细看还是有一点线索的。

  • 重点是为什么第二个指针里存的最后两个char类型的变量,这里就是apple对对象进行了属性重排,也就是内存对齐,验证了之前我们的猜测。

结构体是不进行属性重排的,只有对象才会进行属性重排

第五个指针:double类型

apple对double,float进行了优化并不是直接存储。

验证一下:


总结

apple通过内存对齐将对象的内存进行了极致的压榨,这一点提现在很多地方,真的值得我们学习。

相关文章

网友评论

    本文标题:OC底层探索05-内存对齐

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