IOS底层(六): alloc相关: 内存对齐

OC底层源码/原理合集

建议先看下
IOS底层(三): alloc相关1.初探 alloc, init, new源码分析,
IOS底层(四): alloc相关: 对象属性在内存中的布局

首先先介绍IOS中3种获取内存方法

  • sizeof
  • class_getInstanceSize
  • malloc_size

sizeof

sizeof得到的是: 数据类型占用空间的大小

sizeof是一个操作符, 不是函数, 我们一般用sizeof计算内存大小时, 传入的主要对象是数据类型, 这个在编译器的编译阶段就会确定而不是运行时确定。

class_getInstanceSize

class_getInstanceSize 得到的是: 实例对象中成员变量的内存大小

class_getInstanceSizeruntime提供的api, 用于获取类的实例对象所占用的内存空间的大小, 并返回具体的字节数。

malloc_size

class_getInstanceSize 得到的是: 系统实际分配的内存大小

这个是由系统完成的, 涉及16字节内存对齐

三种获取内存方法打印结果

关于字节对齐, 我之前写的都是类的, 这次看下结构体的字节对齐, 例:

        struct Mystruct1 {
            char a;     // 1字节
            double b;   // 8字节
            int c;      // 4字节
            short d;    // 2字节
        } Mystruct1;
        
        struct Mystruct2 {
            double b;
            int c;
            short d;
            char a;
        } Mystruct2;
        
        NSLog(@"结构体1占用的内存大小 %lu", sizeof(Mystruct1));
        NSLog(@"结构体2占用的内存大小 %lu", sizeof(Mystruct2));
结果

为什么会是这个样子呢? 看到这个结果至少2个问题

  • 1.这个内存是怎么计算的?
  • 2.结构体1跟2的区别只是换了一个char位置, 为什么内存会变?

内存对齐原则

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

  • 数据成员对齐可以理解为min(m, n)公式, 其中m表示当前成员开始位置, n表示当前成员所需要的位数。如果满足 m 整除 n (m % n == 0), nm位置开始存储, 反之m循环+1, 直至可以整除, 从而确定了当前成员位置。

  • 数组成员为结构体, 当结构体嵌套结构体时, "成员"的结构体的自身长度为"成员"结构体中最大成员的内存大小, 例如结构体a嵌套结构体b,b中有char、int、double等,则b的自身长度为8

  • 结构体的内存大小必须为结构体最大成员内存大小的整数倍, 不足需要补齐

验证内存对齐原则

先看下ios中数据类型的占用内存大小, 方便我们之后计算


字节数对照表

针对于上面那个例子, 先看下这个图便于理解


结构体例子计算
结构体1
  • a: char类型占1字节, 因为 0 % 1 = 0, 即, 0储存a
  • b: double类型占8字节,
    1 % 8 = 1, 不满足 +1,
    2 % 8 = 2, 不满足 +1
    ......
    8 % 8 = 0, 满足, 8储存b
  • c: int类型占6字节, 16 % 4 == 0, 16存储c
  • d: short类型占2字节, 20 % 2 == 0, 20存储d

结构体1需要内存22, 由于最大字节数为8, 结构体必须是8的倍数, 所以需要向上取整, 固最终结果为24

结构体2
  • b: double类型占8字节, 0 % 8 == 0, 0储存b
  • c: int类型占6字节, 8 % 4 == 0, 8存储c
  • d: short类型占2字节, 12 % 2 == 0, 12存储d
  • a: char类型占1字节, 因为 14 % 1 = 0, 即, 14储存a

结构体1需要内存15, 由于最大字节数为8, 结构体必须是8的倍数, 所以需要向上取整, 固最终结果为16

之前的问题解决了, 我们接下来看下结构体嵌套结构体

结构体嵌套结构体
        struct Mystruct1{
            char a;     //1字节
            double b;   //8字节
            int c;      //4字节
            short d;    //2字节
        }Mystruct1;

        struct Mystruct2{
            double b;   //8字节
            int c;      //4字节
            short d;    //2字节
            char a;     //1字节
        }Mystruct2;

        struct Mystruct3 {
            double b;
            int c;
            short d;
            char a;
            struct Mystruct2 str;
        } Mystruct3;

看下结构体3(结构体3为结构体2基础上嵌一个结构体2), 如果按照我们之前的定义

  • 结构体2为16, 且最大字节数8, 那么作为成员结构体2, 字节数为也为8, 结构体3最大字节数为8

  • str: struct类型占16字节, 16 % 16 == 0, 固从16起

结构体3需要内存32, 由于最大字节数为8, 结构体必须是8的倍数, 32满足, 不需要取整, 固结果应为32, 我们验证一下

结构体嵌结构体
结构体嵌入结构体例子计算

我们再做一下验证一下

      struct Mystruct4{
            int a;
            struct Mystruct5{
                double b;
                short c;
            }Mystruct5;
        }Mystruct4;
  • 先看内嵌结构体5中最大为double, 8字节,
    0 % 8 = 0, 0开始放doubleb,
    8 % 2 = 0, 8放shortc, 别忘了整体需要最大值整数倍, 那么结构体5需要16字节

  • 结构体4中, 首先最大为结构体5的8字节,
    0 % 4 = 0, 0开始放inta,
    8 % 8 = 0, 8开始放 结构体5, 总共需要 24

  • 因为24是8的整数倍, 故占用内存大小为24

验证一下


结构体嵌结构体

内存优化(属性重排)

上面我们看到结构体1与结构体2里面属性是一样的, 只有排列位置不一样, 结果占用内存结果也不一样。所以结构体内存大小与结构体成员内存大小的顺序有关

  • 如果是结构体中数据成员是根据内存从小到大的顺序定义的,根据内存对齐规则来计算结构体内存大小,需要增加有较大的内存padding即内存占位符,才能满足内存对齐规则,比较浪费内存

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

结构体的看完了, 我们看下类中的属性重排, 例如我们定义一个SATest

属性重排例子

main中给一些值, 然后我们读一下内存

属性重排例子

这个我们我们可以看到 name, age, hobby都读到可, 但是height, c1, c2并没有读取到, 而且isa旁边的内存段读出来是一串数字?

原因是由于苹果系统对属性进行重排, 0x0000001200006261这一串就是age, c1, c2age占4个字节,c1,c2占1个字节,通过4+1+1的方式,按照8字节补齐的方式存储在同一块内存中, 我们打印一下

属性重排例子

这里留意一下char类型是以ASCII码形式显示, 而地址为0x0000000000000000,表示person中还有未赋值

总结:

  • 大部分内存都是固定以内存块进行读取
  • 尽管我们在内存中采用内存对齐形式, 但是系统会自动对属性进行重排, 以此来优化内存
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容