美文网首页
二、 字节对齐

二、 字节对齐

作者: Mlqq | 来源:发表于2020-09-25 15:33 被阅读0次

探索OC类的大小要先来说一下结构体,因为OC类底层都是以结构体的形式存在的。

1 结构体大小-字节对齐

我们先定义一下两个结构体:

//1、定义两个结构体
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;

//计算 结构体占用的内存大小
NSLog(@"%lu-%lu",sizeof(Mystruct1),sizeof(Mystruct2));

一下是输出结果:

image.png 从结构体的成员来说都是一样的,就是顺序不一样而已,为什么大小就不一样呢?这个就是字节对齐导致的。

内存对齐规则

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

(1)数据成员对⻬规则:结构(struct)(或联合(union))的数据成员,第
一个数据成员放在offset0的地方,以后每个数据成员存储的起始位置要
从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,
结构体等)的整数倍开始(比如int4字节,则要从4的整数倍地址开始存
储。min(当前开始的位置mn)m=9 n=4 9 10 11 12

(2)结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从
其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b
里有char,int ,double等元素,那b应该从8的整数倍开始存储.)

(3)收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍.不足的要补⻬。

验证对齐规则

下表是各种数据类型在ios中的占用内存大小,根据对应类型来计算结构体中内存大小

image.png 根据上面的规则解释一下为什么Mystruct1Mystruct2两个结构体大小不一样
根据内存对齐规则计算MyStruct1的内存大小,详解过程如下:
  • 变量a:占1字节,从offset0开始,01的整数倍,所以0存储a
  • 变量b:占8字节,应该从offset1开始存,但是1不是8的整数倍,所要往后移动到8的倍数,即从offset8开始,89101112131415位置存放b
  • 变量c:占4字节,从16开始,164的倍数,所以16171819位置存放c
  • 变量d:占2字节,从20开始,202的倍数,所以2021位置存放d
  • Mystruct1大小应该满足是其最大成员变量b所占字节数8的整数倍,所以Mystruct1的大小为24

根据内存对齐规则计算MyStruct2的内存大小,详解过程如下:

  • 变量b:占8字节,从offset为0开始,0-7存储b
  • 变量c:占4字节,从8开始,84的倍数,所以8-11存储c
  • 变量d:占2字节,从12开始,122的倍数,所以12-13存储d
  • 变量a:占1字节,从14开始,141的倍数,所以14存储a
  • Mystruct2大小应该满足是其最大成员变量b所占字节数8的整数倍,所以Mystruct2的大小为16

结构体套嵌结构体

我们在定义一个结构体3如下:

struct Mystruct3 {
    int c;
    struct Mystruct1 str;
    short d;
}Mystruct3;

我们来分析一下Mystruct3的大小:

  • 变量c:占4位,0-3存储
  • 结构体str:根据规则2,开始位置应该为Mystruct1内最大变量的整数倍,Mystruct1最大变量为8,所以开始位置为8的整数倍即位置8,即是位置8存储Mystruct1里的变量a,接下来该存储Mystruct1里的变量b,存储变量b的位置也要是b大小的整数倍,所以从位置16开始,16-23存储变量b,同理24-27存储变量Mystruct1里的变量c28-29存储Mystruct1里的变量d。因为变量str也是结构体,所以也要满足字体对齐的原则,其大小也要是其内部最大变量b大小8的整数倍,因此要扩展为32。因此 8-32 存储变量str
  • 变量d:占2位,33-34存储
  • Mystruct3的大小为其内部最大成员变量的整数倍,Mystruct3最大的成员变量是其结构体成员变量Mystruct1里面的变量b,所以Mystruct3大小为8的倍数40
看一下运行结果: image.png

2 OC类的字节对齐

alloc&init&new流程提到过alloc流程里面的三个重要的放方法: size = cls->instanceSize(extraBytes);obj = (id)calloc(1, size);obj->initInstanceIsa(cls, hasCxxDtor);,这里我们分析size = cls->instanceSize(extraBytes);这个方法。
从这个方法跟进去就是:

image.png 继续跟进去: image.png 继续进去: image.png 这里就是OC类对齐的方法:
static inline size_t align16(size_t x) {
    return (x + size_t(15)) & ~size_t(15);
}

这就是16字节对齐,也就是说OC类大小开辟的空间大小是16的倍数。这里就不验证了,感兴趣的朋友可以自己验证一下。

3 OC内存重排

我们先创建一个类MlqqObject,属性参考图中,创建是一个实力对象objc,然后对属性复制,然后打印objc的内存分布,如下图:

image.png 来分析一下内存情况:
  • 18字节:这个字节里面存的是对象的isa,关于这部分知识在另外一篇文章详细介绍,这里就简单说明一下。

  • 28字节:我们打印一下

    image.png 发现是一个不认识的数字
  • 38字节:同样打印一下

    image.png 看到结果是19,说明这里存的是height
  • 48字节:同样打印一下

    image.png 结果是Mlqq,说明这个字节存的是name
  • 58字节:同样打印一下

    image.png 结果是北京,说明这个字节存的是address

6以后的字节都打印一下,都没有看到ageweight的值。其实ageweight的值就是在第28字节,我么把它拆为两部分分别打印:

image.png

正是ageweight的值,这就是系统对属性重排的结果。ageweight都是int类型,占4字节,两个int是8字节,正好可以用一个8字节来存存储,就没必要一个int也要占8字节。
height也是int类型,也是占4字节,为什么不是ageheight放在一起呢,我们改变一下属性声明的顺序

image.png

改变属性声明的顺序后,内存分布的顺序也改变了,现在第28字节存的是weightheight,第38字节存的是age,后面才是NSString类型。
另外同样是第28字节,也就是重排的字节,声明在前面的内存中在后面。weight声明在height前面,内存0x0000001300000011,前4个字节是height,后4个字节是weight,为了更好的说明,我们再增加多一点属性,按照下面方式:

image.png
  • 28字节:0x6867666564636261从后往前代表的分别是abcdefgh,的ascii值,跟声明的顺序是相反的。
  • 38字节:0x0000001100006a69,前4个字节0x00000011的值是17,代表weight。后4个字节0x006b6a69,从后往前代表的是ijk,这一个里面存了3个长度的属性分别是int4字节,short2字节,char1字节,排列的顺序是先排多字节的属性,后排小字节属性。
  • 48字节:0x0000001200000013,前4个字节0x00000012的值是18,代表age,后4个字节0x00000013的值是19,代表height跟声明的顺序是相反的。

内存重排总结:

  • 当属性中有属性所占字节数小于8时,需要重排
  • 重排时属性占字节数小的属性排在前面,满足字节数之和为8字节的属性排在一起
  • 重排在同一个8字节的属性,属性字节数不相等,字节数大的排在前面,字节数小的排在后面
  • 重排在同一个8字节的属性,属性字节数都相等,声明在后的属性排在前面
  • 不发生重排的属性按声明的顺序排列

相关文章

  • 二、 字节对齐

    探索OC类的大小要先来说一下结构体,因为OC类底层都是以结构体的形式存在的。 1 结构体大小-字节对齐 我们先定义...

  • 字节对齐与大端小端与内存区域划分

    字节对齐 C语言字节对齐C语言字节对齐/7213465 大端小端 字节序(大小端)详解从高低地址和高低位开始理解(...

  • iOS 技术

    结构体的字节对齐和OC对象的字节对齐? instance(实例对象)、class(类对象)、meta-class(...

  • 字节对齐

    为什么字节对齐: 体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,...

  • 字节对齐

  • 字节对齐

    什么叫做字节对齐? 对齐跟数据在内存中的位置有关。如果一个变量的内存地址正好位于它长度的整数倍,他就被称做自然对齐...

  • 字节对齐

    字节对齐的细节和编译器实现相关,一般满足三个准则: 1.结构体变量的首地址能够被其最宽的基本类型成员的大小所整除 ...

  • 字节对齐

    总原则:结构体变量占据的内存单元的个数应当大于等于其内部所有数据成员占据内存单元数的和。 出于效率的考虑,C预言引...

  • 字节对齐

    概述 字节对齐也称为字节填充,它是C++编译器的一种技术手段,主要目的是为了在空间和复杂度上达到平衡。简单而言,是...

  • #字节对齐

    [TOC] 什么是字节对齐(可以跳过) 现代计算机中内存空间都是按照字节(byte)划分的,从理论上讲似乎对任何类...

网友评论

      本文标题:二、 字节对齐

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