iOS-内存对齐

3,144 阅读2分钟

iOS内存对齐原则:

  • 1、数据成员对其规则:结构体(struct)或联合体(union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如数组、结构体等)的整数倍开始。

    eg: int为4字节,则要从4的整数倍地址开始存储

  • 2、结构体作为成员:如果一个结构体内部包含其他结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。

    eg: struct a里包含struct b,b中包含其他char、int、double等元素,那么b应该从8(double的元素大小)的整数倍开始存储

  • 3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的需要补齐。

类对象的属性内存开辟

struct StructOne {
    char a;         // 1字节
    double b;       // 8字节
    int c;          // 4字节
    short d;        // 2字节
} MyStruct1;

struct StructTwo {
    double b;       // 8字节
    int c;          // 4字节
    char a;         // 1字节
    short d;        // 2字节
} MyStruct2;

struct StructOThree {
    double b;       // 8字节
    char a;         // 1字节
    int c;          // 4字节
    short d;        // 2字节
} MyStruct3;

NSLog(@"%lu---%lu---%lu",sizeof(MyStruct1),sizeof(MyStruct2),sizeof(MyStruct3));

打印结果: 24---16---24

从内存对齐原则来看,上面三个结构体在内存中应该是这样的:

类对象的内存开辟

Person *p = [Person alloc];
p.name = @"Kaemi";  //  NSString  8
p.age = 18;         //  int       4
p.height = 188;     //  long      8
p.hobby = @"game";  //  NSString  8

NSLog(@"申请内存大小为:%lu——-系统开辟内存大小为:%lu",class_getInstanceSize([p class]),malloc_size((__bridge const void *)(p)));

打印结果: 申请内存大小为:40---系统开辟内存大小为:48

根据内存对齐原则,我们很容易得出,对象申请的内存大小为40个字节,那么为什么系统会为我们开辟出48个字节的内存呢?

查看malloc源码:

接下来我们就得使用 lldb 命令来查看这段代码的具体实现

(lldb) p zone->calloc
(void *(*)(_malloc_zone_t *, size_t, size_t)) $0 = 0x000000010031cd14 (.dylib`default_zone_calloc at malloc.c:249)

确定default_zone_calloc,再搜索它的实现源码

static void *
default_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
{
	zone = runtime_default_zone();
	
	return zone->calloc(zone, num_items, size);
}

继续用lldb查看:

(lldb) p zone->calloc
(void *(*)(_malloc_zone_t *, size_t, size_t)) $0 = 0x000000010031e33f (.dylib`nano_calloc at nano_malloc.c:878)

接着查看nano_calloc的实现源码

继续:
最终,我们会看到这块代码:

/*
 * #define SHIFT_NANO_QUANTUM		4
 * #define NANO_REGIME_QUANTA_SIZE	(1 << SHIFT_NANO_QUANTUM) // 16
 */
static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
	size_t k, slot_bytes;
	if (0 == size) {
		size = NANO_REGIME_QUANTA_SIZE; 
	}
	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;
}

结论:

我们可以判断出,系统为对象本身做了16字节的对齐。 这也就是为什么我们申请40个字节的内存大小,而系统为我们开辟48个字节内存的原因。

系统为什么要这么做的原因也很简单,它是为了在访问对象的时候提高访问效率(空间换时间),同时也可以有效避免访问溢出。