OC对象(二)-- 内存对齐和calloc中的16字节对齐

2020-09-08  本文已影响0人  过气的程序员DZ

OC对象(一)-- alloc和init底层到底在干嘛
OC对象(二)-- 内存对齐和calloc中的16字节对齐
OC对象(三)-- isa结构分析

内存对齐初探


实例对象在内存中的布局,是被系统优化过的,不会按照属性定义的顺序在内存中开辟空间。
举个例子:
定义一个Person类,里面包括一些属性

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nick;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) char ch1;
@property (nonatomic, assign) char ch2;
@end

@implementation Person
@end

初始化实例对象,给属性进行赋值:

Person *p = [Person alloc];
p.name = @"DragonetZ";
p.nick = @"DZ";
p.age = 18;
p.ch1 = 'a';
p.ch2 = 'z';
NSLog(@"p:%@", p);

使用lldb指令x/4gx p查看实例对象的内存情况,p是实例对象的指针变量,打印结果如下:

(lldb) x/4gx p
0x600000683e20: 0x00000001061fb6c8 0x0000001200007a61
0x600000683e30: 0x00000001061f9018 0x00000001061f9038

如图中展示,内存中的排序和类型属性定义的顺序不一致。

拓展-lldb命令解释


上文用了lldb指令x/4gx p,这里做一个简单解释:

打印内容:


内存对齐原则


OC中,实例对象其实就是struct类型,因此我们研究一下struct是如何进行内存对齐的

简单的小案例

struct Struct1 {
    double a;
    char b;
    int c;
    short d;
}stu1;

struct Struct2 {
    double a;
    int b;
    char c;
    short d;
}stu2;

NSLog(@"%lu - %lu", sizeof(stu1), sizeof(stu2));

定义两个struct,每个struct中都有几个不同类型的成员,打印两个struct的内存占用情况。


stu1占用24个字节,stu2占用16字节。

此处可以使用下图来自己先计算一下:


基础数据类型内存占用表

原理知识点

  1. struct第一个数据成员,从偏移量offset的0位开始。后续的成员从自身的整数倍的偏移位置开始。
  2. 计算出来的总大小,需要是最大成员的整数倍。

用stu1解释说明:

struct Struct1 {
    double a;
    char b;
    int c;
    short d;
}stu1;

再来看看stu2:

struct Struct2 {
    double a;
    int b;
    char c;
    short d;
}stu2;

扩展-struct嵌套

如果struct a中有另一个struct b作为它的成员,那么偏移量offset就取struct b中最大成员的整数倍开始

struct Struct2 {
    double a;
    int b;
    char c;
    short d;
}stu2;

struct Struct3 {
    char a;
    short b;
    struct Struct2 c;
}stu3;

struct Struct2前面分析结构:占用16个字节,最大成员是8字节
分析stu3

OC底层的优化

上面的例子中stu1和stu2两个struct可以说是差不多,但是一个占用24,一个占用16。但是我们在OC类的时候,属性顺序是不受影响的。说明苹果底层是对我们内存开辟进行优化过的。这里可以通过示例对象内存打印中可以发现,文章开始的例子中:


Person类中属性age、ch1、ch2的值都存放在内存中第二个位置(0x0000001200007a61)中。

calloc


之前文章alloc和init底层到底在干嘛!中分析了alloc流程,先是用16字节对齐的方式计算出size,然后调用calloc函数来开辟内存空间

//16字节对齐获取到size
size = cls->instanceSize(extraBytes);

//根据size开辟内存空间
obj = (id)calloc(1, size);

此时产生一个问题,如果传入的size没有进行16字节对齐,也就是说传入的不是16字节的倍数,会是什么情况?

测试代码

#import <malloc/malloc.h>

void *temp = calloc(1, 40);
NSLog(@"%lu", malloc_size(temp));

调用calloc方法,第二个参数传入40,注意这个值不是16的倍数。
运行结果:


通过结果可以看出,calloc里面也会进行16字节对齐,接下来我们来找找这16字节对齐的代码。

查看源码

首先先看看calloc函数在哪个源码中,用⌘+鼠标左键,属于malloc源码中


下载源码libmalloc-283.100.6

源码中大致的流程,如图:


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; // 16
    }
    k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // (size + 16 -1)右移4位 SHIFT_NANO_QUANTUM=4
    slot_bytes = k << SHIFT_NANO_QUANTUM;                           // 左移4位
    *pKey = k - 1;                                                  // Zero-based!

    return slot_bytes;
}

这个函数就是16字节对齐的函数:

上一篇 下一篇

猜你喜欢

热点阅读