logo

百度APP iOS端内存优化-原理篇(中)

作者:沙与沫2023.02.08 20:23浏览量:329

简介:内存分配函数alloc源码分析

在上一篇文章中介绍了Mach虚拟内存(百度APP iOS端内存优化-原理篇上),本篇将详细分析内存分配函数alloc源码。

二、 内存分配函数alloc源码分析

为了了解内存分配底层原理,我们从alloc函数源码分析说起,下载objc开源库,然后从iOS做内存分配的函数[NSObject alloc] 开始一起分析。

2.1 objc_rootAlloc函数

+(id)alloc {
    return _objc_rootAlloc(self);
}
id  _objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

调用函数callAlloc,并传入两个值checkNil为false以及allocWithZone为true。

2.2 callAlloc函数

static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
    if (slowpath(checkNil && !cls)) return nil;
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        return _objc_rootAllocWithZone(cls, nil);
    }
#endif
   /* 省略 */ 
}

首先OBJC2_宏定义,代表objc的版本,现在编译器使用的都是Objective-C2.0,进入if语句,slowpath(告诉编译器,传入的条件结果为假的可能性很大),因为objc_rootAlloc传入的checkNil为false,所以不会返回nil,接着执行fastpath(告诉编译器,传入的条件结果为真的可能性很大), 这个判断就是去检测传入的这个类是否实现了allocWithZone方法, 如果没有实现进入下一个函数。

2.3 objc_rootAllocWithZone函数

调用_class_createInstanceFromZone

NEVER_INLINE id  _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
    // allocWithZone under __OBJC2__ ignores the zone parameter
    return _class_createInstanceFromZone(cls, 0, nil,
                                         OBJECT_CONSTRUCT_CALL_BADALLOC);
}

2.4 class_createInstanceFromZone核心函数

static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
                              int construct_flags = OBJECT_CONSTRUCT_NONE,
                              bool cxxConstruct = true,
                              size_t *outAllocatedSize = nil)
{    //断言机制,防止类并发创建
    ASSERT(cls->isRealized());
    //读取类的标志位,加速类对象的创建
    bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();
    size_t size;
    // 计算内存空间大小
    size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;
    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        obj = (id)calloc(1, size);
    }
     /* 省略 */ 
}

我们可以看到先调用instanceSize函数计算出创建对象需要的内存空间大小,然后再调用malloc_zone_calloc或者calloc去分配内存空间。

2.5 instanceSize计算内存空间大小

inline size_t instanceSize(size_t extraBytes) const {
    if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
        return cache.fastInstanceSize(extraBytes);
    }
    size_t size = alignedInstanceSize() + extraBytes;
    if (size < 16) size = 16;
    return size;
}

为了减少计算时间,先判断缓存是否有值,如果有先从缓存取值,否则需要进入计算逻辑,从上面的代码逻辑中我们看到入参extraBytes值为0,返回值就是alignedInstanceSize,源码如下:


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

我们知道OC类结构中,data字段存储类相关信息,其中ro数据结构存储了当前类在编译期就已经确定的属性、方法以及遵循的协议,所以从当前对象所属类的ro中获取instanceSize代表了分配对象所需内存空间。


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

接下来调用word_align做内存对齐操作,从上述源码可以发现类对象的创建是按16字节对齐,不足16字节的返回16,这是iOS在堆上分配OC对象基本原则,都是以16倍数做分配。

2.6 malloc_zone_calloc函数

malloc_zone_calloc或者calloc去分配内存空间,这两个函数正是libmalloc中重要的内存分配函数,libmalloc库中路径src/malloc可以看到源码
https://opensource.apple.com/source/libmalloc/libmalloc-317.40.8/src/malloc.c.auto.html

void *
calloc(size_t num_items, size_t size)
{
  return _malloc_zone_calloc(default_zone, num_items, size, MZ_POSIX);
}

MALLOC_NOINLINE
static void *
_malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size,
    malloc_zone_options_t mzo)
{
  MALLOC_TRACE(TRACE_calloc | DBG_FUNC_START, (uintptr_t)zone, num_items, size, 0);

  void *ptr;
  if (malloc_check_start) {
    internal_check();
  }

  ptr = zone->calloc(zone, num_items, size);

  if (os_unlikely(malloc_logger)) {
    malloc_logger(MALLOC_LOG_TYPE_ALLOCATE | MALLOC_LOG_TYPE_HAS_ZONE | MALLOC_LOG_TYPE_CLEARED, (uintptr_t)zone,
        (uintptr_t)(num_items * size), 0, (uintptr_t)ptr, 0);
  }

  MALLOC_TRACE(TRACE_calloc | DBG_FUNC_END, (uintptr_t)zone, num_items, size, (uintptr_t)ptr);
  if (os_unlikely(ptr == NULL)) {
    malloc_set_errno_fast(mzo, ENOMEM);
  }
  return ptr;
}

void *
malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
{
  return _malloc_zone_calloc(zone, num_items, size, MZ_NONE);
}

相关文章推荐

发表评论