OC底层原理(二).内存分配与内存对齐

从内存分配开始

在上一篇的流程图中,我们看到最后的流程中,在_class_createInstanceFromZone,我们分为三步:

这一篇我们来分析获取需要分配的大小,以及具体如何分配内存,也就是我们的第1步和第2步。

分析instanceSize函数的实现

size_t instanceSize(size_t extraBytes) const {
   if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
       return cache.fastInstanceSize(extraBytes);
   }

   size_t size = alignedInstanceSize() + extraBytes;
   // CF requires all objects be at least 16 bytes.
   if (size < 16) size = 16;
   return size;
}

我们从alignedInstanceSize开始看,这个方法中

uint32_t alignedInstanceSize() const {
   return word_align(unalignedInstanceSize());
}

这个方法中的unalignedInstanceSize我们再来看看

uint32_t unalignedInstanceSize() const {
   ASSERT(isRealized());
   return data()->ro()->instanceSize;
}

我们来分析data()->ro()->instanceSize,data()实现为:

class_rw_t *data() const {
   return bits.data();
}

class_ro_t是类在编译器存储类信息的数据结构,它里面包含了类的实例变量、方法列表、协议列表等,class_rw_t是用来存储 在dyld的_map_images方法中将分类的各种信息与class_ro_t合并后的信息。

data()->ro()->instanceSize也就是获取对象所有实例变量需要的存储大小。

我们再看word_align的实现

#   define WORD_MASK 7UL
static inline uint32_t word_align(uint32_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}

这个方法先将大小加上7,然后和~WORD_MASK进行逻辑与,由于7的二进制为0000 1111,对它取反后1111 0000,再与前面进行与,那么我们的内存分配的大小就是8的倍数了,也就是8字节对齐

最后,if (size < 16) size = 16;这一步就是如果不足16字节,那么我们就补足16,所以一个OC对象,至少是16个字节。

整个步骤流程如下:


OC底层原理(二).内存分配与内存对齐

instanceSize流程

获取内存大小的三种方式

我们在OC中获取内存大小的三种方式分别是:

我们创建一个LWTestClass来测试

@interface LWTestClass : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) long height;

@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LWTestClass *person = [LWTestClass alloc];
        person.name      = @"Cooci";
        person.nickName  = @"KC";
        
        NSLog(@"%@ - %lu - %lu - %lu",person,sizeof([LWTestClass class]),class_getInstanceSize([LWTestClass class]),malloc_size((__bridge const void *)(person)));
    }
    return 0;
}

打印结果: - 8 - 40 - 48

如上面的结果

为什么是48字节?

我们在开篇中所说的第一步是获取对象所需空间大小,这里我们只能确定对象内存的内存布局是8字节对齐,这也是class_getInstanceSize获取到的内存大小,malloc_size获取到的是系统分配的内存大小,这是属于我们第二步中obj = (id)calloc(1, size);的内容,calloc方法的分析我们需要用libmalloc源码,具体的调用顺序我们不一一列举,大致的调用过程如以下流程图与核心代码。

OC底层原理(二).内存分配与内存对齐

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;
   //size为0,补为16
    if (0 == size) {
        size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
    }
    //size加上15再右移4位,将低4位给丢弃
    k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // round up and shift for number of quanta
    //再左移4位,这样得到的大小就是16的倍数
    //内存分配也就是以16字节对齐了
    slot_bytes = k << SHIFT_NANO_QUANTUM;                           // multiply by power of two quanta size
    *pKey = k - 1;                                                  // Zero-based!

    return slot_bytes;
}

所以,在前面的例子中,我们计算出对象所需的内存字节大小为40,进行16字节对齐后,我们实际分配的内存大小为48.

为什么系统分配内存要以16字节对齐

这个没找到具体的答案,猜想如下:

这段有想法的大佬希望可以留言给予指导

内存对齐

上面我们说内存对齐,那么内存对齐有什么规则呢?为什么要内存对齐呢?

内存对齐规则

struct/class/union内存对齐原则有四个:

内存对齐实践

我们设置3个结构体,并分别打印它们所占字节大小

struct TestStruct1{
    double a;
    int b;
    bool c;
    short d;
}st1;

struct TestStruct2{
    int a;
    double b;
    bool c;
    short d;
}st2;

struct TestStruct3{
    int a;
    double b;
    struct TestStruct1 st;
    bool c;
    short d;
}st3;

int main(int argc, const char * argv[]) {

    NSLog(@"st1:%lu,st2:%lu,st3:%lu",sizeof(st1),sizeof(st2),sizeof(st3));
    //打印结果:st1:16,st2:24,st3:40
    return 0;
}

从结果可以看到,st1和st2结构体中所含有的数据类型与个数相同,但位置不同,它们所占的大小也变得不一致了。

下面我们来分析它们分别的内存构成:

struct TestStruct1

struct TestStruct2

struct TestStruct3

三个结构体的内部布局如下图


OC底层原理(二).内存分配与内存对齐

结构体内存构成

为什么要内存对齐

回答这个问题,我们要从内存、CPU以及平台三个方向来回答

1.平台

移植原因:不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2.CPU原因

3.内存原因

我们先讲讲内存结构



作者:默默_David
链接:https://www.jianshu.com/p/d285a33bf88f
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

展开阅读全文

页面更新:2024-04-25

标签:分配   内存   倍数   变量   字节   底层   原理   大小   成员   位置   结构   数据

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top