一、对齐原则:
1:结构体(struct) and 联合体(union)的数据成员,第一个数据成员放在offset为0的位置,后面每个数据成员存储的位置要从该成员size or 其子成员size(只要该成员有子成员,比如array,struct) 的整数倍开始(比如int为4字节,则要从4的整数倍开始存储)。
2:struct作为成员:若一个struct里有struct成员,则struct成员要从其内部最大元素size的整数倍地址开始存储。(struct a里存有struct b,b里有char,int ,double等,那应该从8整数倍开始存储。)
3:struct的总size,sizeof的结果,必须是其内部最大成员的整数倍,不足的补齐。
基础:arm64下,double 8字节 int 4字节 char 1字节 short 2字节。
struct成员,内存存储优化 如下:
对象的属性间,一般以8字节对齐;对象与对象之间以16字节对齐,之所以8,16字节对齐,是因为,对象存储时可能会发生溢出,所以多给一点空间。
对象需要的内存空间,以空间换取时间为8的倍数,8字节对齐,但 最少为16字节。
开辟内存给实例对象的源码如下:内存对齐的算法 &运算
uint32_t alignedInstanceSize() {//对齐的实例对象Size
return word_align(unalignedInstanceSize());
}
// 开辟给实例对象空间的Size
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;//小于16 置为16
return size;
}
//内存对齐的算法 &运算 (此处结果为8的倍数,或者16的倍数)
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
A Sample:
//对象需要40字节,系统分配了48字节
LGTeacher *p = [LGTeacher alloc];
// isa // 8
p.name = @"LG_Cooci"; // 8
p.age = 18; // 4 need align 4
p.height = 185; // 8
p.hobby = @"女"; // 8 sum = 40
// 40 - 48
NSLog(@"%lu - %lu", class_getInstanceSize([p class]), malloc_size((__bridge const void *)(p)));
查看class_getInstanceSize源码如下。
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
//result:打印结果与注释推理结果一样,符合内存对齐算法所得结果
malloc_size:系统分配内存的函数,源码追踪下获得内存分配源码。
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
size_t k, slot_bytes;
//size:40
if (0 == size) {
size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
}
// 算法 (40 + 16 -1)>> 4 (左移4位) 然后右移4位 变为48.
// 左移4位 右移4位 =》 16字节对齐
k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // round up and shift for number of quanta
slot_bytes = k << SHIFT_NANO_QUANTUM; // multiply by power of two quanta size
*pKey = k - 1; // Zero-based!
return slot_bytes;
}
//conclusion:内存分配给对象是以16字节对齐的方式分配内存的。
PS:源码追踪流程借鉴这位兄台的1.3 探索 calloc 底层。写的很漂亮哦。