OC 底层探索 02、开辟空间 - 字节对齐 + 内存对齐
一、字节对齐
1、为什么要进行字节对齐处理呢?
性能,快,以空间换取时间。
可以想象2个场景,场景1:当我们的CPU去读取内存时,每次读取的大小如果是不确定的(8、7、3、9... ...),那么除非每次读取都要重新改变自己的读取方式否则读取的数据就会读错到其他数据上去。
场景2,如果我们所存储的对象以固定的大小存储时,8/16,那么读取数据时,只需要按部就班去取 不必反复改变读取方式。
场景对比,自然是场景2 更快捷方便。
2、Apple 的 16 字节对齐算法
Apple 的 objc 最新源码中目前是16 字节对齐,在此之前是8字节对齐(注意!真正的对象内存大小仍是8字节对齐的,后面会分析的)。代码如下:
static inline size_t align16(size_t x) { return (x + size_t(15)) & ~size_t(15); }
算法流程如下图:--> 16后抹零
3、验证
运行源码 demo:
如上图,MyPerson 有3个属性,+ isa = 4*8 = 32
下图情况:
此时,好像有2个问题,问题001:我们每次都给对象16字节的对齐处理,未使用的空间岂不浪费了吗?还有打印时,问题002:数据存储的顺序为什么和我们代码不一致呢?后面继续探索...
二、对象的大小
运行下图代码,MyPerson中有 5个属性(有一个继承来的 isa):
结果分析
3.1)sizeof():类型大小. --> 例:sizeof(int)-4 / sizeof(double)-8 --> sizeof(person)->person 类型(MyPerson *) 指针8
3.2)class_getInstanceSize()
对象的真正需要的内存大小 40 --> 8字节对齐;
objc 源码:--> 8字节对齐: (x + 7) & (7取反) --> 算法和上面16字节对齐一样
3.3)开辟内存大小:malloc 源码分析
系统会开辟空间48 --> 16字节对齐,使之有更大的容错空间
malloc --> 16字节对齐算法:
二、字节对齐会造成严重的内存空间浪费问题吗?
1、空间浪费了吗?
我们给 MyPerson 类添加2个 char 类型的 a1 a2,如下图,运行:
通过以上,我们可以得出,同一个地址中存了3个值,系统并不会一味的对所有对象都分配相同空间,它对内存是做了优化使用的。
2、内存对齐
内存对齐原则
1. 数据成员对齐规则:
结构体(struct)的数据成员,第一个数据从 0 的位置开始存放,后面数据依次存。每个成员存放的起始位置 必须是这个成员的大小的 整数倍。
结构体作为成员,结构体 s2 作为 结构体 s 的成员,s2 的存储起始位置 应该是 s2 内部最大的成员大小的整数倍。
2、整个结构体大小
结构体的总大小(sizeof()),必须是结构体内部最大成员的整数倍,不足的补齐。
结构体内存对齐:
示例2则:
struct MyStruct1 { double a; // 8 (0-7) char b; // 1 [8 1] (8) int c; // 4 [9 4] 9... -->大于9的4的倍数:12 --> (12 13 14 15) short d; // 2 [16 2] (16 17) }struct1; // 内部需要的大小为: 17 // 最大属性 : 8 // 结构体大小需要是 其内部最大属性 的整数倍: 大于17的8的倍数 --> 24 // // 15 --> 16 struct MyStruct2 { double a; //8 (0-7) int b; //4 (8 9 10 11) char c; //1 (12) short d; //2 13 (14 15) - 16 }struct2;
我们可以发现,上面2个结构体内部成员是一样的,只是顺序不同。但是 1 比 2 多占用24-16=8 字节的内存 --> 浪费了呀??
--> 苹果系统做了优化,进行了 属性重排 --> 上面问题002 的答案可知
注意:结构体不存在苹果的这个优化的,不同顺序大小不同。
调试命令地址: GDB to LLDB command map
不同数据类型字节大小:
x 的使用 读取内存
x /nuf <addr>
n 表示要显示的内存单元的个数
-----------------------------------------
u 表示一个地址单元的长度:
b表示单字节
h表示双字节
w表示四字节
g表示八字节
-----------------------------------------
f 表示显示方式, 可取如下值:
x 按十六进制格式显示变量
d 按十进制格式显示变量
u 按十进制格式显示无符号整型
o 按八进制格式显示变量
t 按二进制格式显示变量
a 按十六进制格式显示变量
i 指令地址格式
c 按字符格式显示变量
f 按浮点数格式显示变量