iOS-内存对齐原则

899 阅读2分钟

一、对齐原则:

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成员,内存存储优化 如下:

struct成员占位对比
二、对象申请的内存 VS 系统分配的内存

对象的属性间,一般以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 底层。写的很漂亮哦。