OC对象探究02:内存分析

2020-10-10  本文已影响0人  开发狗

1.对象的内存地址分布

首先创建一个Person类,其中包含了namenicknameagec1c2五个属性。此时创建一个person对象,并对其属性进行赋值,然后打印person对象的内存地址。

WechatIMG19.jpeg person对象.png
通常在调试窗口使用p person 命令打印对象person的内存地址。使用x/4gxx/8gx + 内存地址$11,(其中$11为图中person 类的标识符 )可以来打印具体的地址下的内存地址情况,如图。

2.结构体的内存对齐

通过查看源码可以看出,OC对象的内存对齐来自于结构体的内存,所以研究结构体。结构体指针的大小为8字节,结构体的内存大小取决于其内部属性。

struct Struct1 {
    double a;   // 8  0 1 2 3 4 5 6 7
    char b;     // 1  8
    int c;      // 4  12 13 14 15
    short d;    // 2  16 17    总计需要:17   总计开辟:24
} struct1;

struct Struct2 {
    double a;   // 8  0 1 2 3 4 5 6 7
    int b;      // 4  8 9 10 11
    char c;     // 1  12
    short d;    // 2  14 15      总计需要:15   总计开辟:16
    struct Struct1 struct1;
} struct2;

打印如下:

2020-10-02 11:52:16.814642+0800 内存对齐[2209:74659] 24-40

首先分析结构体struct1
在64位架构下,按照定义计数从0位开始

对于结构体struct2来说:

3. instanceSize方法分析

instanceSize方法是在初始化alloc方法中的方法_class_createInstanceFromZone中调用的,用来计算对象开辟的内存大小。首先通过宏定义得fastPath()来判断是否有较大概率进行快速路径,此时使用的是gcc中定义的__builtin_except(N, 1)表示较大概率执行,此时返回调用cache中的方法fastInstanceSize来计算内存大小,在fastInstanceSize中会走计算size_t size = _flags & FAST_CACHE_ALLOC_MASK通过按位与的计算方式将_flags与宏定义的FAST_CACHE_ALLOC_MASK 0x1ff8计算得出内存大小,然后调用align16方法,在align16方法中使用(x+size_t(15) & ~size_t(15))取16位以下为0的方式,进行了按照16位的倍数的内存对齐

x  = 7    0000 0111
15        0000 1111
x + 15    0001 0110
~15       1111 0000

x + size_t(15) & ~size_t(15)
16        0001 0000

可以看出该算法是开辟的内存使用实际开辟的内存进行16的倍数扩容。

4. calloc 分析

calloc调用流程.png
static void *nano_malloc(nanozone_t *nanozone, size_t size)
{
    if (size <= NANO_MAX_SIZE) {
        void *p = _nano_malloc_check_clear(nanozone, size, 0);
        if (p) {
            return p;
        } else {
            /* FALLTHROUGH to helper zone */
        }
    }
    malloc_zone_t *zone = (malloc_zone_t *)(nanozone->helper_zone);
    return zone->malloc(zone, size);
}

重点是调用了_nano_malloc_check_clear方法进行对p指针进行赋值,在初始化时size 为 0,一定是小于NANO_MAX_SIZE = 256的,所以此处一定会返回p

void *ptr;
size_t slot_key;
size_t slot_bytes = segregated_size_to_fit(nanozone, size, &slot_key); // Note slot_key is set here
mag_index_t mag_index = nano_mag_index(nanozone);
nano_meta_admin_t pMeta = &(nanozone->meta_data[mag_index][slot_key]);
ptr = OSAtomicDequeue(&(pMeta->slot_LIFO), offsetof(struct chained_block_s, next));

所以重点在一个slot_bytes的计算,然后对 ptr的赋值,而slot_bytes的计算是重点,接下来研究segregated_size_to_fit方法

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; // Historical behavior
    }
    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;
}

在代码中 NANO_REGIME_QUANTA_SIZE(1 << SHIFT_NANO_QUANTUM)也就是16SHIFT_NANO_QUANTUM4,所以在初始化时,通过size = NANO_REGIME_QUANTA_SIZEsize赋值为16(size + NANO_REGIME_QUANTA_SIZE -1) >> SHIFT_NANO_QUANTUM 是将低四位进行抹零,slot_bytes = k << SHIFT_NAMO_QUANTUM 将k进行左移四位,通过这两步将高位保留低位抹零,从而保证为16的倍数。

上一篇 下一篇

猜你喜欢

热点阅读