前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OC底层探索05-内存对齐OC底层探索05-内存对齐

OC底层探索05-内存对齐OC底层探索05-内存对齐

作者头像
用户8893176
发布2021-08-09 11:26:40
4450
发布2021-08-09 11:26:40
举报
文章被收录于专栏:小黑娃Henry
上篇对对象实际内存占用和内存分配计算做了解释,但是留了一个小坑:对象属性的大小计算。

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

内存对齐

普通结构体,例1:
代码语言:javascript
复制
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
代码语言:javascript
复制
struct HRStruct {
    double a;   //8
    int c;  //4
    char b; //1
    char d;    //1
}HRStruct;
  • 直接计算: 8 + 4 + 1 + 1 = 14

仔细观察这个结构体中的变量是相同的,不同的只是顺序.

以下是输出结果:

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

结构体嵌套,例3
代码语言:javascript
复制
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:
代码语言:javascript
复制
struct HRStruct {
    int c;  //4
    double a;   //8
    char b; //1
    char d;    //1
}HRStruct;
  • 通过内存布局图,直观的查看内存占用情况
例2:
代码语言:javascript
复制
struct HRStruct {
    double a;   //8
    int c;  //4
    char b; //1
    char d;    //1
}HRStruct;
  • 通过内存布局图,直观的查看内存占用情况
例3:
代码语言:javascript
复制
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即可满足堆存对齐规则,这种方式就是苹果中采用的,利用空间换时间,将类中的属性进行重排,来达到优化内存的目的

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

我们通过一个对象的内存情况来验证一下我们的猜测:
代码语言:javascript
复制
@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通过内存对齐将对象的内存进行了极致的压榨,这一点提现在很多地方,真的值得我们学习。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/9/12 下,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 上篇对对象实际内存占用和内存分配计算做了解释,但是留了一个小坑:对象属性的大小计算。
  • 内存对齐
    • 普通结构体,例1:
      • 普通结构体,例2
        • 结构体嵌套,例3
          • 内存对齐原则
            • 例子验证
              • 例1:
              • 例2:
              • 例3:
          • 内存优化(属性重排)
            • 我们通过一个对象的内存情况来验证一下我们的猜测:
              • 第一个指针:isa指针
              • 第二个指针:短类型重排
              • 第五个指针:double类型
          • 总结
          相关产品与服务
          对象存储
          对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档