iOS 内存管理(三): 内存管理方案 源码分析

·  阅读 474

准备

一、 ARC&MRC

  • ARCLLVMRuntime配合的结果。
  • ARC中禁止手动调用retain/release/retainCount/dealloc
  • ARC新加了weakstrong属性关键字。

二、 alloc

关于alloc的相关内容可以看下面这三篇文章:

三、retain、release

3.1 retain

源码中查看retain函数:

inline id 
objc_object::retain()
{
    ASSERT(!isTaggedPointer());
    return rootRetain(false, RRVariant::FastOrMsgSend);
}
复制代码

查看rootRetain函数:

ALWAYS_INLINE id
objc_object::rootRetain(bool tryRetain, objc_object::RRVariant variant)
{
    // 如果是TaggedPointer,return
    if (slowpath(isTaggedPointer())) return (id)this;

    bool sideTableLocked = false;
    bool transcribeToSideTable = false;

    isa_t oldisa;
    isa_t newisa;

    oldisa = LoadExclusive(&isa.bits);

    do {
        transcribeToSideTable = false;
        newisa = oldisa;
        // 如果不是nonpointer isa
        if (slowpath(!newisa.nonpointer)) {
            ClearExclusive(&isa.bits);
            if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
            // 通过 sidetable 进行retain操作
            else return sidetable_retain(sideTableLocked);
        }
        // don't check newisa.fast_rr; we already called any RR overrides
        // 如果正在释放,不进行引用计数操作
        if (slowpath(newisa.isDeallocating())) {
            ClearExclusive(&isa.bits);
            if (sideTableLocked) {
                ASSERT(variant == RRVariant::Full);
                sidetable_unlock();
            }
            if (slowpath(tryRetain)) {
                return nil;
            } else {
                return (id)this;
            }
        }
        uintptr_t carry;
        // 通过移动RC_ONE大小移动到extra_rc的位置
        newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc++

        // 如果extra_rc存满了
        if (slowpath(carry)) {
            // newisa.extra_rc++ overflowed
            if (variant != RRVariant::Full) {
                ClearExclusive(&isa.bits);
                return rootRetain_overflow(tryRetain);
            }
            // Leave half of the retain counts inline and 
            // prepare to copy the other half to the side table.
            if (!tryRetain && !sideTableLocked) sidetable_lock();
            sideTableLocked = true;
            transcribeToSideTable = true;
            // extra_rc中引用计数减为一半
            newisa.extra_rc = RC_HALF;
            // has_sidetable_rc 置为true
            newisa.has_sidetable_rc = true;
        }
    } while (slowpath(!StoreExclusive(&isa.bits, &oldisa.bits, newisa.bits)));
    // 存满的情况下,另一半存到sidetable中
    if (variant == RRVariant::Full) {
        if (slowpath(transcribeToSideTable)) {
            // Copy the other half of the retain counts to the side table.
            // 另一半存到sidetable中
            sidetable_addExtraRC_nolock(RC_HALF);
        }

        if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
    } else {
    }

    return (id)this;
}
复制代码
  • TaggedPointer,什么也不做直接return
  • nonpointer isa,通过sidetable_retain进行引用计数操作。
  • nonpointer isa
    • 如果正在释放,不进行引用计数操作。
    • 通过移动RC_ONE大小移动到extra_rc所在位置,进行extra_rc++操作。
    • 如果extra_rc存满了,extra_rc中引用计数减为一半,has_sidetable_rc设置为true,另一半引用计数通过sidetable_addExtraRC_nolock函数存到sidetable中。

来看一下sidetable_retain函数:

id
objc_object::sidetable_retain(bool locked)
{
#if SUPPORT_NONPOINTER_ISA
    ASSERT(!isa.nonpointer);
#endif
    // 获取本对象所在SideTable
    SideTable& table = SideTables()[this];
    
    if (!locked) table.lock();
    // 获取引用计数表
    size_t& refcntStorage = table.refcnts[this];
    // 将引用计数加进去
    if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
        refcntStorage += SIDE_TABLE_RC_ONE;
    }
    table.unlock();

    return (id)this;
}
复制代码
  • 获取本对象所在SideTable
  • 获取引用计数表,将引用计数加进去。

再来看一下sidetable_addExtraRC_nolock函数:

bool 
objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)
{
    ASSERT(isa.nonpointer);
     // 获取本对象所在SideTable
    SideTable& table = SideTables()[this];
    
    // 获取引用计数表
    size_t& refcntStorage = table.refcnts[this];
    size_t oldRefcnt = refcntStorage;
    // isa-side bits should not be set here
    ASSERT((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
    ASSERT((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);

    if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true;

    uintptr_t carry;
    // 将引用计数加进去
    size_t newRefcnt = 
        addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry);
    if (carry) {
        refcntStorage =
            SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK);
        return true;
    }
    else {
        refcntStorage = newRefcnt;
        return false;
    }
}
复制代码
  • 获取本对象所在SideTable
  • 获取引用计数表,将引用计数加进去。

看到这里应该会有个疑问,当extra_rc中空间满了的时候,为什么要extra_rc和散列表中各存一半呢。

  • 原因是这样的,通过isa可以很容易的拿到extra_rc,通过extra_rc进行引用计数的存储是很方便的。散列表是先拿到SideTable这张表,再在表中拿到引用计数表,才能进行操作,表操作还要做加锁和解锁操作,非常浪费性能。
  • 那为什么只存一半,而不是全部都存储过去呢,因为现在只是考虑了++操作,如果都存过去了,当进行--操作时,又要去SideTable中进行操作,同样浪费性能,这样各留一半,++--extra_rc中都有引用计数进行操作。

3.2 release

源码中查看release函数:

inline void
objc_object::release()
{
    ASSERT(!isTaggedPointer());
    rootRelease(true, RRVariant::FastOrMsgSend);
}
复制代码

查看rootRelease函数:

ALWAYS_INLINE bool
objc_object::rootRelease(bool performDealloc, objc_object::RRVariant variant)
{
    if (slowpath(isTaggedPointer())) return false;

    bool sideTableLocked = false;
    isa_t newisa, oldisa;
    oldisa = LoadExclusive(&isa.bits);

retry:
    do {
        newisa = oldisa;
        // 如果不是nonpointer isa
        if (slowpath(!newisa.nonpointer)) {
            ClearExclusive(&isa.bits);
            // 通过 sidetable 进行release操作
            return sidetable_release(sideTableLocked, performDealloc);
        }
        // 如果正在释放,不进行引用计数操作
        if (slowpath(newisa.isDeallocating())) {
            ClearExclusive(&isa.bits);
            if (sideTableLocked) {
                ASSERT(variant == RRVariant::Full);
                sidetable_unlock();
            }
            return false;
        }

        // don't check newisa.fast_rr; we already called any RR overrides
        uintptr_t carry;
        // 通过移动RC_ONE大小移动到extra_rc的位置,进行extra_rc--操作
        newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);
        // 如果向下溢出,跳到 underflow
        if (slowpath(carry)) {
            // don't ClearExclusive()
            goto underflow;
        }
    } while (slowpath(!StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits)));

    ...

 underflow:
    // newisa.extra_rc-- underflowed: borrow from side table or deallocate

    // abandon newisa to undo the decrement
    newisa = oldisa;

    // 判断 has_sidetable_rc 是否为true
    if (slowpath(newisa.has_sidetable_rc)) {
        if (variant != RRVariant::Full) {
            ClearExclusive(&isa.bits);
            return rootRelease_underflow(performDealloc);
        }

        // Transfer retain count from side table to inline storage.

        if (!sideTableLocked) {
            ClearExclusive(&isa.bits);
            sidetable_lock();
            sideTableLocked = true;
            // Need to start over to avoid a race against 
            // the nonpointer -> raw pointer transition.
            oldisa = LoadExclusive(&isa.bits);
            goto retry;
        }
        
        // 从 sidetable 中取一半的引用计数
        auto borrow = sidetable_subExtraRC_nolock(RC_HALF);

        bool emptySideTable = borrow.remaining == 0; // we'll clear the side table if no refcounts remain there

        if (borrow.borrowed > 0) {
            // Side table retain count decreased.
            // Try to add them to the inline count.
            bool didTransitionToDeallocating = false;
            // 给 extra_rc 存储 (一半 - 1)
            newisa.extra_rc = borrow.borrowed - 1;  // redo the original decrement too
            newisa.has_sidetable_rc = !emptySideTable;

            bool stored = StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits);

            ...
        }
        else {
            // Side table is empty after all. Fall-through to the dealloc path.
        }
    }

deallocate:
    // Really deallocate.

    if (slowpath(sideTableLocked)) sidetable_unlock();

    if (performDealloc) {
        ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
    }
    return true;
}
复制代码
  • TaggedPointer,什么也不做直接return
  • nonpointer isa,通过sidetable_release进行引用计数操作。
  • nonpointer isa
    • 如果正在释放,不进行引用计数操作。
    • 通过移动RC_ONE大小移动到extra_rc所在位置,进行extra_rc--操作。
    • 如果extra_rc向下溢出,跳到underflow
      • 如果has_sidetable_rctrue,从sidetable中取一半的引用计数,将(一半 - 1)存储到extra_rc中。
      • 否则通过objc_msgSend消息发送调用dealloc函数。

四、 SideTables 结构分析

先看下SideTables的定义:

static StripedMap<SideTable>& SideTables() {
    return SideTablesMap.get();
}
复制代码
template<typename T>
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
    enum { StripeCount = 8 };
#else
    enum { StripeCount = 64 };
#endif
...
};
复制代码
  • SideTables的本质就是个hash表,并通过StripedMap进行了封装,每个元素是SideTable表,真机时开辟表的最大个数是8个,其他情况下开辟表的最大个数是64个。
  • 为什么是多张SideTable,而不是一张呢,因为如果所有的对象公用一张表,我们使用表的时候要做开锁和解锁操作,这样性能消耗就会比较大。

再看下SideTable的定义:

struct SideTable {
    spinlock_t slock;
    RefcountMap refcnts;
    weak_table_t weak_table;
    ...
};
复制代码
  • slock是自旋锁,refcnts是引用计数表,weak_table是弱引用表。

由上面源码可以得到下面的结构图:

image.png

五、 rootRetainCount

查看rootRetainCount函数:

inline uintptr_t 
objc_object::rootRetainCount()
{
    if (isTaggedPointer()) return (uintptr_t)this;

    sidetable_lock();
    isa_t bits = __c11_atomic_load((_Atomic uintptr_t *)&isa.bits, __ATOMIC_RELAXED);
    if (bits.nonpointer) {
        // 先取isa中的extra_rc
        uintptr_t rc = bits.extra_rc;
        // 如果has_sidetable_rc为true,再把散列表中的引用计数加进来
        if (bits.has_sidetable_rc) {
            rc += sidetable_getExtraRC_nolock();
        }
        sidetable_unlock();
        return rc;
    }

    sidetable_unlock();
    return sidetable_retainCount();
}
复制代码

六、 弱引用表

下面代码是我们平时弱引用的写法:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *objc = [[NSObject alloc] init];
        __weak typeof(id) weakObjc = objc;
    }
    return 0;
}
复制代码

通过汇编调试,可以看到弱引用是调用了objc_initWeak函数:

image.png

6.1 弱引用 源码分析

进入objc_initWeak函数:

id
objc_initWeak(id *location, id newObj)
{
    if (!newObj) {
        *location = nil;
        return nil;
    }

    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object*)newObj);
}
复制代码

objc_initWeak函数真实实现是调用了storeWeak函数,其实objc_destroyWeak函数的内部也是调用的storeWeak函数,只不过传的参数有所不同。

进入objc_destroyWeak函数:

void
objc_destroyWeak(id *location)
{
    // 销毁函数也是调用的storeWeak,只不过传入的参数有所不同
    (void)storeWeak<DoHaveOld, DontHaveNew, DontCrashIfDeallocating>
        (location, nil);
}
复制代码

进入storeWeak函数:

num CrashIfDeallocating {
    DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};
template <HaveOld haveOld, HaveNew haveNew,
          enum CrashIfDeallocating crashIfDeallocating>
static id 
storeWeak(id *location, objc_object *newObj)
{
    ASSERT(haveOld  ||  haveNew);
    if (!haveNew) ASSERT(newObj == nil);

    Class previouslyInitializedClass = nil;
    id oldObj;
    SideTable *oldTable;
    SideTable *newTable;

 retry:
    // destroy 过来时 haveOld 为true
    if (haveOld) {
        oldObj = *location;
        oldTable = &SideTables()[oldObj];// 取出相应的散列表
    } else {
        oldTable = nil;
    }
    // init 过来时 haveNew 为true
    if (haveNew) {
        newTable = &SideTables()[newObj];// 取出相应的散列表
    } else {
        newTable = nil;
    }

    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);

    if (haveOld  &&  *location != oldObj) {
        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
        goto retry;
    }
    
    // 如果类还没注册,进行相关的注册
    if (haveNew  &&  newObj) {
        Class cls = newObj->getIsa();
        if (cls != previouslyInitializedClass  &&  
            !((objc_class *)cls)->isInitialized()) 
        {
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            class_initialize(cls, (id)newObj);
            
            previouslyInitializedClass = cls;

            goto retry;
        }
    }

    // destroy 过来时 haveOld 为true,进行相关移除
    if (haveOld) {
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }

    // init 过来时
    if (haveNew) {
        // 关键函数 weak_register_no_lock,进行相关注册操作
        newObj = (objc_object *)
            weak_register_no_lock(&newTable->weak_table, (id)newObj, location, 
                                  crashIfDeallocating ? CrashIfDeallocating : ReturnNilIfDeallocating);
        
        if (!newObj->isTaggedPointerOrNil()) {
            newObj->setWeaklyReferenced_nolock();
        }
        
        *location = (id)newObj;
    }
    else {
        // No new value. The storage is not changed.
    }
    
    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);

    callSetWeaklyReferenced((id)newObj);

    return (id)newObj;
}
复制代码

storeWeak中的关键函数是weak_register_no_lock,点击查看:

id 
weak_register_no_lock(weak_table_t *weak_table, id referent_id, 
                      id *referrer_id, WeakRegisterDeallocatingOptions deallocatingOptions)
{ // referent_id:(id)newObj referrer_id:location

    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;
    
    ...
    
    weak_entry_t *entry;
    
    // 如果在weak_table表中,已经有 referent 对应的weak_entry_t了
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        // 直接将 referrer 添加到 weak_entry_t 中
        append_referrer(entry, referrer);
    } 
    else {
        // 以referent和referrer为参数,创建 weak_entry_t
        weak_entry_t new_entry(referent, referrer);
        // 检测3/4扩容操作
        weak_grow_maybe(weak_table);
        // 将 weak_entry_t 插入到 weak_table 中
        weak_entry_insert(weak_table, &new_entry);
    }

    return referent_id;
}
复制代码

查看weak_entry_for_referent函数:

static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
    // 获取 weak_entries
    weak_entry_t *weak_entries = weak_table->weak_entries;

    if (!weak_entries) return nil;

    size_t begin = hash_pointer(referent) & weak_table->mask;
    size_t index = begin; 
    size_t hash_displacement = 0;
    // 计算出index
    while (weak_table->weak_entries[index].referent != referent) {
        index = (index+1) & weak_table->mask;
        if (index == begin) bad_weak_table(weak_table->weak_entries);
        hash_displacement++;
        if (hash_displacement > weak_table->max_hash_displacement) {
            return nil;
        }
    }
    // 返回 index 下标处的 weak_entry_t
    return &weak_table->weak_entries[index];
}
复制代码

6.2 弱引用表 结构分析

查看weak_table_t结构:

struct weak_table_t {
    weak_entry_t *weak_entries;
    size_t    num_entries;
    uintptr_t mask;
    uintptr_t max_hash_displacement;
};
复制代码

weak_table_t中存储了weak_entry_t的列表,查看weak_entry_t结构:

struct weak_entry_t {
    // 在堆上开辟的对象地址
    DisguisedPtr<objc_object> referent;
    union {
        struct {
            // 弱引用指针地址列表
            weak_referrer_t *referrers;
            uintptr_t        out_of_line_ness : 2;
            uintptr_t        num_refs : PTR_MINUS_2;
            uintptr_t        mask;
            uintptr_t        max_hash_displacement;
        };
        struct {
            // out_of_line_ness field is low bits of inline_referrers[1]
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
    };
};
复制代码

弱引用表结构图:

image.png

6.3 小结

  • SideTables中先获取obj所在的SideTable
  • SideTable中获取到弱引用表:weak_table_t
  • weak_table_t中查找obj对应的weak_entry_t
    • 如果有,将weak指针地址直接添加到weak_entry_t中。
    • 如果没有
      • 新创建一个weak_table_t,并将objweak指针地址添加进去。
      • 检测存储空间如果大于3/4进行扩容操作。
      • 将新建的weak_entry_t插入到weak_table中。

函数调用流程图:

image.png

七、 dealloc

7.1 dealloc 源码分析

查看dealloc

- (void)dealloc {
    _objc_rootDealloc(self);
}
复制代码

查看_objc_rootDealloc

void
_objc_rootDealloc(id obj)
{
    ASSERT(obj);
    obj->rootDealloc();
}
复制代码

查看rootDealloc

inline void
objc_object::rootDealloc()
{    
    // 判断是否为 TaggerPointer 内存管理方案,是的话直接 return
    if (isTaggedPointer()) return;  // fixme necessary? * 
    
    if (fastpath(isa.nonpointer  &&          // 如果 isa 为 nonpointer
                 !isa.weakly_referenced  &&  // 没有弱引用
                 !isa.has_assoc  &&          // 没有关联对象
                 !isa.has_cxx_dtor  &&       // 没有 C++ 的析构函数
                 !isa.has_sidetable_rc))     // 没有额外采用 SideTabel 进行引用计数存储
    {
        assert(!sidetable_present());
        free(this);               // 如果以上条件成立,直接调用 free 函数销毁对象
    }   
    else {
        object_dispose((id)this); // 如果以上条件不成立,调用 object_dispose 函数
    }   
}   
复制代码

查看object_dispose

id 
object_dispose(id obj)
{
    if (!obj) return nil;
    objc_destructInstance(obj);    
    free(obj);
    return nil;
}
复制代码

查看objc_destructInstance

void *objc_destructInstance(id obj) 
{
    if (obj) {
        // Read all of the flags at once for performance.
        bool cxx = obj->hasCxxDtor();
        bool assoc = obj->hasAssociatedObjects();
        
        // This order is important.
        if (cxx) object_cxxDestruct(obj);           // 如果有 C++ 的析构函数,调用 object_cxxDestruct 函数
        if (assoc) _object_remove_assocations(obj); // 如果有关联对象,调用 _object_remove_assocations 函数,移除关联对象    
        obj->clearDeallocating();                   // 调用 clearDeallocating 函数
    }

    return obj;
}
复制代码

查看clearDeallocating

inline void 
objc_object::clearDeallocating()
{
    // 如果 isa 不是 nonpointer
    if (slowpath(!isa.nonpointer)) {     
        // 调用 sidetable_clearDeallocating 函数
        sidetable_clearDeallocating();   
    }
    // 如果 isa 是 nonpointer,且有弱引用或者有额外使用 SideTable 存储引用计数
    else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) { 
        // 调用 clearDeallocating_slow 函数
        clearDeallocating_slow();        
    }
}
复制代码

查看sidetable_clearDeallocating

void 
objc_object::sidetable_clearDeallocating()
{
    // 获取 SideTable
    SideTable& table = SideTables()[this]; 

    table.lock();
    // 获取 refcnts
    RefcountMap::iterator it = table.refcnts.find(this); 
    if (it != table.refcnts.end()) {
        if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
            // 调用 weak_clear_no_lock:将指向该对象的弱引用指针置为 nil
            weak_clear_no_lock(&table.weak_table, (id)this); 
        }   
        // 调用 table.refcnts.erase:从引用计数表中擦除该对象的引用计数
        table.refcnts.erase(it); 
    }
    table.unlock();
}
复制代码

查看clearDeallocating_slow

NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
    ASSERT(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc))
    
    // 获取 SideTable
    SideTable& table = SideTables()[this]; 
    table.lock();
    // 如果有弱引用
    if (isa.weakly_referenced) { 
        // 调用 weak_clear_no_lock:将指向该对象的弱引用指针置为 nil
        weak_clear_no_lock(&table.weak_table, (id)this);
    }
    // 如果有使用 SideTable 存储引用计数
    if (isa.has_sidetable_rc) {  
        // 调用 table.refcnts.erase:从引用计数表中擦除该对象的引用计数
        table.refcnts.erase(this);
    }
    table.unlock();
}
复制代码

7.2 小结

  • 判断 5 个条件
    • 1.isa为nonpointer;2.没有弱引用;3.没有关联对象;4.没有C++的析构函数;5.没有额外采用SideTabel进行引用计数存储;
    • 如果这 5 个条件都成立,直接调用free函数销毁对象,否则调用object_dispose做一些释放对象前的处理;
  • 调用object_dispose进行相关处理
    • 1.如果有C++的析构函数,调用object_cxxDestruct
    • 2.如果有关联对象,调用_object_remove_assocations函数,移除关联对象;
    • 3.调用weak_clear_no_lock将指向该对象的弱引用指针置为nil;
    • 4.调用table.refcnts.erase从引用计数表中擦除该对象的引用计数(如果isa为nonpointer,还要先判断isa.has_sidetable_rc
  • 调用free函数销毁对象。
分类:
iOS
标签:
分类:
iOS
标签: