前言
通过前文对alloc流程的探索,已经清楚了alloc方法的调用流程,但是创建对象应该开辟多大的内存空间依然是个疑问,带着这样的疑问,开始下面的探索
1.首先创建一个XQPerson类,不声明任何属性和方法
2.只声明属性
3.只声明成员变量
4.只声明方法
5.声明成员变量和方法
结论:
1.未声明任何属性和方法时,类的实力对象的内存大小为 isa
指针8字节,添加成员变量或属性后实例对象内存isa
指针的8字节加上各成员变量所占字节数之和然后以8字节对齐。
2.只有成员变量会影响对象的内存空间,方法不会对对象内存空间有任何影响(实例方法在类的method_list
,类方法在元类的method_list
)
字节对齐
1.8字节对齐 上文提到,对象所占内存空间为8字节对齐,我们找到对象内存8字节对齐的代码如下:
(x + WORD_MASK) & ~WORD_MASK
,x
是参数,类型是size_t
,代表当前对象声明各成员变量的字节数之和unalignedInstanceSize
,WORD_MASK
是宏定义,64位系统下值是7,假设x = 20
,那么表达式就是:
(20 + 7) & ~7
= 0001 0100 + 0000 0111 & 1111 1000
= 0001 1011 & 1111 1000
= 0001 1000 = 24
2.16字节对齐
(x + size_t(15)) & ~size_t(15)
,x
是参数,类型是size_t
,代表当前对象声明各成员变量字节数之和进行8字节对齐的值,假设x = 24
那么表达式就是:
(24 + 15) & ~15
= 0001 1000 + 0000 1111 & 1111 0000
= 0010 0111 & 1111 0000
= 0010 0000 = 32
内存对齐的原因: 如下一个类的对象,isa占用8个字节,各成员变量分别占用8,1,4个字节,一共占据21个字节,如果按照图一方式读取数据,需要多次更换读取字节长度,
@interface XQPerson : NSObject
@property(nonatomic,copy)NSString* name;
@property(nonatomic,assign)char sex;
@property(nonatomic,assign)int height;
@end
结构体内存对齐
内存对齐原则:
-
数据成员对齐规则:结构体(struct)或联合体(union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的存储位置要从该成员大小或成员的子成员大小(只要该成员有子成员,比如说是数组结构体等)的整数倍开始(比如int是4字节,则要从4的整数倍地址开始存储)。
-
结构体作为成员,如果一个结构体里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储(struct a里面有struct b,b里面有char,int,double等元素,那b应该从8的整数倍开始存储)。
-
收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员大小的整数倍,不足的要补齐。
对象内存对齐
我们知道,对象的成员是8字节对齐,那么对象与对象之间是也是8字节对齐吗?
通过下面的打印可知,对象与对象之间是16进制对齐
系统在什么时候进行的16进制对齐呢?
通过源码跟流程可以找到,在 instanceSize
方法中,当有缓存size时进行16字节对齐。
但是当没有缓存,会怎么进行16字节对齐呢?
我们通过注释return cache.fastInstanceSize(extraBytes);
执行后续代码,发现返回的size为 isa
与各成员变量字节数相加后8进制对齐,那么此时使用 size
为24执行 calloc
后对象是8字节对齐还是16进制对齐呢?
通过上面的打印,发现,此时对象仍然是16字节对齐。那么此时是在什么时候进行的16字节对齐呢?
跳转进入 calloc
方法,发现此方法源码在malloc
我们找到libmalloc源码
执行 calloc(1,24)
通过跟流程,找到16进制对齐方法如下
执行完 calloc
函数后,打印结果如下:
malloc
流程图:
总结:
- 只有成员变量会影响对象的内存
- 对象的成员变量,8字节对齐,相加不满8字节的优化放在一起,不足的补0。
- 对象与对象,16字节对齐。