OC底层原理02:内存对齐

1.为何要内存对齐?

struct Struct1{
    double a; // 8字节 
    char b;   // 1字节 
    int c;    // 4字节 
    short d;  // 2字节 
}struct1;
复制代码

内存存取粒度:CPU读取内存时,以'一块一块'的进行读取,‘块’的大小能够是一、二、四、八、16...个字节,这样的块就是内存读取粒度markdown

  • 假如在没有内存对齐的状况下,读取如上的数据时,会是什么样的呢?

CPU在读取数据时,首先从内存读取粒度8开始读取,读出了a的数据,而后使用一样的读取粒度读取后边的数据时,发现b、c、d读不到,因此要修改内存读取粒度,从8降到4,这时读取b就包含了c的数据,这是不对的,直到修改内存读取粒度为1时,才能读获得b,接下来读c时,内存读取粒度就不够了,须要加大粒度值两次到4,才能读取到c...优化

由上咱们能够知道,每次在读取时,咱们可能都要同步调整内存读取粒度,才不会读取越界,这样CPU的内存访问速度会受到很是大的影响,读取的时间会很长,而且出错的风险也会很大.spa


2.内存对齐的规则

  1. 结构(struct)或联合(union))的数据成员,第一个数据成员放在offset为0的地方,之后每一个数据成员存储的起始位置要从该成员大小或者成员的子成员的整数倍开始
  2. 若是一个结构体里存在结构体成员,则结构体成员要从其内部最大成员数据类型所占内存大小的整数倍地址开始存储
  3. 结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足要补齐

由内存对齐的规则,咱们再来看上边的结构体是如何对齐存储的.3d

    1. double a
    由规则1,第一个数据成员a,会从偏移量0的位置开始,占据了8个字节

    1. char b

    当存储到char b时,CPU读到了8的位置,8做为1的整数倍(char 为1字节),能够直接从8的位置存储一个字节 指针

    1. int c

    当存储到int c时,CPU读到了9的位置,由规则1数据成员存储的起始位置要从该成员大小的整数倍开始,9并非int大小(4)的整数倍,则padding,十、11同理,直到12的位置,才会开始存储int c code

    1. short d

    当存储到short d时,CPU读到了16的位置,16是short大小(2)的整数倍,能够直接存储 orm

那么,此时这个struct的总大小,由规则3总大小必须是其内部最大成员的整数倍,不足要补齐,其中最大的数据成员是double 8个字节,最后的成员数据d的位置在17.以8字节最大整数倍补齐,因此这个结构体的总大小为24字节.对象

此时内存对齐后读取数据时,使用8个内存读取粒度读取a; 读取b、c时只会修改一次内存读取粒度4,读取b时会把九、十、11也读下来,虽然浪费了三个字节的内存,可是一样也提升了读取的效率.利用空间换时间来进行优化.内存


3. 属性重排优化

struct Struct2{
    double a; // 8字节 
    int c;    // 4字节 
    short d;  // 2字节 
    char b;   // 1字节 
}struct2;
复制代码

Struct2和Struct1中数据成员类型如出一辙,惟一不一样的是排列的顺序差别.依据一样的对齐规则存储结果以下: 因此Struct2的总大小为16字节.同步

这里能够获得这样的结论:结构体内存大小与结构体成员内存大小的顺序有关

  • 案例支持

这里咱们看到0x0000001300006261指针指向的是乱码数据,那么,person的int属性age,char属性c一、c2在哪?

OC对象的本质就是一个结构体

如上图咱们能够看到,person的int属性age,char属性c一、c2重排优化在了一个16位数据中.c1,c2的结果是以ASCII码表示.


结构体拓展1:

struct Struct3{
    double a; 				// 8字节 
    char b;   				// 1字节 
    int c;    				// 4字节 
    short d;  				// 2字节 
    struct Struct1 str;     // 
}struct3;
复制代码

Struct3结构体中也包含了Struct1结构体,这样要如何进行内存对齐呢?

a、b、c、d的内存对齐同上,这里咱们主要看结构体数据成员str.

由内存对齐规则二:

若是一个结构体里存在结构体成员,则结构体成员要从其内部最大成员数据类型所占内存大小的整数倍地址开始存储. Struct1结构体中最大的数据类型是double--8字节,存储到d的位置在17.因此存储Struct1结构体第一个数据double a时,1八、1九、20、2一、2二、23都会被padding.具体以下:

一样由内存对齐规则三:结构体的总大小,必须是其内部最大成员的整数倍,不足要补齐.因此Struct3结构体的总大小为48.


结构体拓展2:

struct Struct1{
    char b;
    int c;
    short d;
}struct1;

struct Struct2{
    int c;
    double a;
    char b;
    struct Struct1 struct1;
    short d;
}struct2;
复制代码

以上两个结构体,一样是结构体struct2中包含告终构体struct1做为数据成员,不过并非放在最后一个位置。这样存储结构体struct2,结果会有什么不同吗?

struct2中,c、a、b存储后的结果到16个字节。重点分析以后如何存储:

  1. 由内存对齐规则二:若是一个结构体里存在结构体成员,则结构体成员要从其内部最大成员数据类型所占内存大小的整数倍地址开始存储.因此struct1开始存储的位置不从17而是从20开始。以下:

注意

此时存储到最后一个数据成员short d时,并非从30开始存储,由于结构体struct1内部还未作到的内存对齐

  1. 由内存对齐规则三: struct的总大小必须是内部最大成员的整数倍,不足要补齐。因此当struct1以4字节最大整数倍补齐后,因此struct1的总大小为12字节。

而后从32开始存储struct2最后一个数据成员short d,存储位置为[32,33]。一样由内存对齐规则三,struct2以8字节最大整数倍补齐后,struct2的总大小为40字节。



数据类型表参考:

相关文章
相关标签/搜索