OC类原理-结构体内存对齐原则

235 阅读5分钟

内存对齐探索 - 初

底层探索一定会涉及到内存,前面alloc探索有讲到内存开辟的原理,那么内存是以什么方式存储排序,内存大小又是跟什么相关呢? 下面我们来进行一番验证。

对象属性展示

当对象只有一个属性变量时

image.png

得到结果如下:

image.png

当对象有两个属性变量时

image.png

得到结果如下:

image.png

我继续测试了将属性变量调整为成员变量的情况、增加方法、增加类方法等效果,这里不一一展示,可以自行验证,从我自己的验证情况得出如下结论:

  • sizeof是计算传进来参数的大小,因为这里是对象指针,指针对应8字节
  • class_getInstanceSize取得的是根据结构体实际计算出来需要的对象大小
  • malloc_size真正系统分配的对象内存大小

增加了一个NSString属性变量,计算出来的对象所需内存增加了8,这个就是一个指针的长度,那么为什么在系统分配过程,内存大小又有所不同呢? 这个就是我们的系统内存对齐原则。

内存对齐探索 - 原则

  1. 数据成员对齐规则:第一个数据成员从0开始,以后每个数据成员以该数据成员大小或子成员大小(数组和结构体有子成员)的整数倍开始。
  2. 结构体作为成员时,则结构体成员要从其内部最大元素大小的整数倍开始存储。
  3. 结构体的总大小(sizeof输出结果),必须是内部最大成员的整数倍,不足补齐

内存对齐探索 - 数据类型对应内存大小

image.png

为什么需要补齐呢?

  • 内存是以字节为单位,CUP存取数据时,是以块为单位,频繁存取未对齐的数据,会增大CUP读取工具,降低性能。而内存对齐,就是我们之前说的以空间换时间的概念。

内存是以字节为基本单位,cpu在存取数据时,是以块为单位存取,并不是以字节为单位存取。频繁存取未对齐的数据,会极大降低cpu的性能。字节对齐后,会减低cpu的存取次数,这种以空间换时间的做法目的降低cpu的开销。 cpu存取是以块为单位,存取未对齐的数据可能开始在上一个内存块,结束在另一个内存块。这样中间可能要经过复杂运算在合并在一起,降低了效率。字节对齐后,提高了cpu的访问速率。

这里举个🌰

struct LGStruct1 {
    double a;       // 8    [0 7]
    char b;         // 1    [8]
    int c;          // 4    (9 10 11 [12 13 14 15]
    short d;        // 2    [16 17] 24
}struct1;

struct LGStruct2 {
    double a;       // 8    [0 7]
    int b;          // 4    [8 9 10 11]
    char c;         // 1    [12]
    short d;        // 2    (13 [14 15] 16
}struct2;

struct LGStruct3 {
    double a;       // 8    [0 7]
    int b;          // 4    [8 11]
    char c;         // 1    [12]
    short d;        // 2    [14 15]
    int e;          // 4    [16 19] 
    struct LGStruct1 str;  // 24    [24 47]  48
} struct3;

我们先来根据对齐原则计算struct1的内存大小

  • 变量a:offset从0开始,存放位置为[0-7]
  • 变量b:offset从8开始(8是数据大小1的倍数),存放位置为[8]
  • 变量c:offset从9开始(9不是数据大小4的倍数,补齐到12开始),存放位置为[12-15]
  • 变量d:offset从16开始(16是数据大小2的倍数),存放位置为[16-17]

我们计算出来结构体大小应该是18,遵循内存对齐原则3,18不是最大数据大小8的倍数,补齐为24,从而得到结构体struct1内存分配大小为24


struct2内存大小的计算如下:

  • 变量a:offset从0开始,存放位置为[0-7]
  • 变量b:offset从8开始(8是数据大小1的倍数),存放位置为[8-11]
  • 变量c:offset从12开始(11是数据大小1的倍数),存放位置为[12]
  • 变量d:offset从13开始(13不是数据大小2的倍数,从14开始),存放位置为[14-15]

我们计算出来结构体大小应该是16,遵循内存对齐原则3,16是最大数据大小8的倍数,从而得到结构体struct2内存分配大小为16.


struct3内存大小的计算如下:

  • 变量a:offset从0开始,存放位置为[0-7]
  • 变量b:offset从8开始(8是数据大小1的倍数),存放位置为[8-11]
  • 变量c:offset从12开始(12是数据大小1的倍数),存放位置为[12]
  • 变量d:offset从13开始(13不是数据大小2的倍数,从14开始),存放位置为[14-15]
  • 变量e:offset从16开始(16是数据大小4的倍数),存放位置为[16-19]
  • 变量str:因为是结构体变量,遵循原则2结构体作为成员时,则结构体成员要从其内部最大元素大小的整数倍开始存储。LGStruct1中最大变量是double,8字节,20不是8的倍数,补位从24开始,存放位置为[24-42]

我们计算出来结构体实际大小应该是43,根据原则3-结构体的总大小(sizeof输出结果),必须是内部最大成员的整数倍,是最大数据大小str(8)的倍数,从而得到结构体struct3内存分配大小为48.