内存对齐
源码地址
官方源码 objc4-818.2
https://opensource.apple.com/tarballs/objc4/
可编译源码
https://github.com/LGCooci/objc4_debug
libmalloc 源码
https://opensource.apple.com/tarballs/libmalloc/libmalloc-317.40.8.tar.gz
为什么需要字节对齐?
内存以字节为基本单位,当CPU存取数据时,以块为单位
读取未对齐数据,需要多次访问内存,极大降低CPU的性能
如果数据存储在自然对齐的位置上,可以降低CPU的存取次数。以空间换取时间,提升CPU的访问速率
数据类型的字节占用
类型 | 32位 | 64位 |
---|---|---|
bool | 1 | 1 |
BOOL | 1 | 1 |
char | 1 | 1 |
short | 2 | 2 |
int | 4 | 4 |
flot | 4 | 4 |
long | 4 | 8 |
NSInteger | 4(int) | 8(long) |
CGFloat | 4(int) | 8(double) |
指针 | 4 | 8 |
double | 8 | 8 |
long | long | 8 |
对象数据成员的字节对齐
直接看 objc 源码中 instanceSize 方法
1 | inline size_t instanceSize(size_t extraBytes) const { |
- fastInstanceSize:从缓存中快速获取内存大小
- alignedInstanceSize:对齐后的对象大小
- extraBytes:额外字节数,传入的值为0
- size:不能小于16字节
进入 alignedInstanceSize
1 | uint32_t alignedInstanceSize() const { |
- word_align: 使用 8 字节对齐
- unalignedInstanceSize: 未对齐的对象大小
可以看到 8 字节对齐算法 ( + 0x00000111 升阶,再和 0x00000111 取反 )
1 |
|
为什么是 8 字节对齐?
如果对齐规则大于8字节,会造成内存空间的浪费。如果小于8字节,读取占8字节的数据类型,需要多次访问内存,故此对齐规则为8字节是最好的选择
数据成员对⻬规则
- 结构(struct)或联合(union)的数据成员,第⼀个数据成员放在offset为0的地⽅,以后每个数据成员存储的起始位置要从该成员⼤⼩或者成员的⼦成员⼤⼩(只要该成员有⼦成员,例如:数组、结构体等)的整数倍开始
例如:int为4字节,则要从4的整数倍地址开始存储。如果当前开始存储的位置为9,需要空出9、10、11,在12的位置才可存储 - 结构体作为成员:如果⼀个结构⾥有某些结构体成员,则结构体成员要从其内部最⼤元素⼤⼩的整数倍地址开始存储
例如:struct a⾥存有struct b,b⾥有char、int、double等元素,那b应该从8的整数倍开始存储 - 收尾⼯作:结构体的总⼤⼩,也就是sizeof的结果,必须是其内部最⼤成员的整数倍,不⾜的要补⻬
案例
案例1:
1 | struct Struct1 { |
案例2:
1 | struct Struct2 { |
案例3:
1 | struct Struct3 { |
获取内存大小的三种方式
- sizeof
- sizeof不是函数,而是一个操作符
- 一般会传入基础数据类型,编译器在编译时期即可确定大小
- sizeof得到的大小,即是该类型占用的空间大小
- class_getInstanceSize
- class_getInstanceSize是runtime提供的api
- 获取实例对象中成员变量的内存大小
- malloc_size
- 获取系统实际分配的内存大小
对象开辟空间的内存对齐 (alloc 分配内存大小)
源码分析
因为我们知道获取实际分配的内存大小是通过 malloc_size 函数,所以直接搜索 malloc_size 函数
1 | size_t |
跟进 find_registered_zone,线索断了
1 | ... |
查了一些资料,发现内存计算是在 segregated_size_to_fit
方法中进行的,找到该方法
1 |
|
size + 15 >> 4 << 4
将 size 右移 4 位,然后左移 4 位。
结论:
对象的内存开辟时 16 进制对齐的
示例
声明一个YCPerson类
1 | @interface YCPerson : NSObject |
创建这个类对象
1 | YCPerson *person = [YCPerson alloc]; |
sizeof 打印的是 person 指针的大小
class_getInstanceSize 为person实际占用的空间
malloc_size 系统分配的内存大小
注:
- 对象继承自 NSObject ,内部有isa指针,占用 8 字节
- 对象会自动对成员变量进行排序,所以对象的成员变量顺序并不会影响对象的大小