这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战 上文我们俩节了内存对齐的一些内容,今天来继续学习。
对齐原理分析
已知系统会根据数据类型跳过部分内存,那跳过的部分为什么不能存储数据?
如上图所示,对于不优化连续存储的情况,CPU读取8~15的内存数据,需要先读取1字节再读取4字节,CPU对于要读取的数据大小是有变化的。而优化后CPU先读取4字节(由于白色3字节空白所以可以直接读取4字节)再读取4字节再这段内存中是没有变化的。相比于第一种情况,优化后CPU要进行的操作变少了,这就实现了通过空间换取时间。
系统内存开辟
分析了内存对齐原理,下面我们来看一下系统是如何开辟内存的。
1. 案例
PDObject定义如下:
@interface PDObject : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) long height;
@end
调用:
#import "PDObject.h"
#import <objc/runtime.h>
#import <malloc/malloc.h>
PDObject *pdObj = [PDObject alloc];
pdObj.name = @"HotpotCat";
pdObj.age = 18;
NSLog(@"sizeof:%zu class_getInstanceSize:%zu malloc:%zu",sizeof(pdObj),class_getInstanceSize([PDObject class]),malloc_size((__bridge const void *)(pdObj)));
那么sizeof、class_getInstanceSize、malloc_size分别输出多少呢? 验证:
sizeof:8 class_getInstanceSize:40 malloc:48
pdObj是一个结构体指针sizeof返回8。class_getInstanceSize由于存在isa和8字节对齐所以返回40 。malloc_size为什么返回48呢?
在系统的内存堆区中对象的内存是16字节对齐,成员变量是以8字节对齐(结构体内部)。对象与对象是16字节对齐。
2.为什么以16字节对齐?
为什么对象不以8字节对齐?而以16字节对齐?
假如一个对象内部成员变量都是8字节大小。
- 以8字节对齐,内部没有多余空间,更容易发生访问错误。
- 以16字节对齐内部有多余空间,不容易发生访问错误。 对于64字节的空间:
16 32 48 64
8 16 24 32 40 48 56 64
以8字节对齐需要访问8次,以16字节对齐需要访问4次
- 明显以16字节对齐访问对象和成员变量碰到一起的概率小了。16字节对齐4次,8字节对齐8次。
- 任何对象都继承自
NSObject,但是很少有对象只有一个isa。所以最小的对象都应该是16。 - 如果用32字节对齐呢?很明显空间浪费太大了。
成员变量字节对齐是8字节对齐,对象的内存对齐市16字节对齐
总结
- 结构体对齐(三个原则)
- 三个原则
- 数据成员对齐规则:从成员大小或者成员的子成员大小的整数倍开始。
- 结构体作为成员:从内部成员最大元素的整数倍地址开始存储。
- 补齐:必须是内部最大成员的整数倍,不足的要补齐。
- 对齐原理:优化CPU读取速度,以空间换时间。
- 结构体嵌套补齐从内部开始补齐。
- 三个原则
- 内存大小获取
sizeof:是运算符,不是函数。获取对象的长度(对象本身)。class_getInstanceSize:获取类的实例所占用的内存大小。大小只与成员变量有关。malloc_size:alloc中实际开辟的空间。
calloc16字节对齐,最小返回16.- 最终分配的内存大小逻辑在
segregated_size_to_fit中。以16字节对齐向上取整 - 为什么以16字节对齐?减少访问错误。
- 最终分配的内存大小逻辑在