美文网首页
iOS探究 --- 内存管理

iOS探究 --- 内存管理

作者: 空空_Jiminy | 来源:发表于2020-05-03 10:13 被阅读0次

    内存布局

    代码段:保存程序二进制。
    bss:一般保存全局静态变量等。
    data:保存初始化的全局变量,静态变量。
    栈:保存函数,方法。iOS开发中一般为0x7段。
    堆:通过alloc出来的对象,保存在堆里。 0x6段

    image.png

    TaggedPointer

    TaggedPointer为苹果为内存优化提供的一个方案。
    主要存储一些小字节的数据。


    image.png
    image.png

    TaggesPointer被设计为2大部分,标志位区和数据区。
    由特殊标志位来标志类型。使得读写更加效率。

    SideTables

    哈希表

    先说说存储结构,实际上在我们学过的数据结构可以归结为两类:连续的的存储结构和不联系的存储结构,其代表分别为数组和链表。而我们学过的堆栈,队列,树,图,都可以用这两种结构来实现。连续的存储结构——数组,在数据的查找和修改上具有很好的优点,很方便,时间复杂度很小。但是在数据的增添和删除上则显得很麻烦,空间复杂度很大。而非连续,非顺序的存储结构——链表恰和数组相反,数据的增添和删除容易,空间复杂度很小,查找和修改复杂,时间复杂度很大。

    那么有没有一种数据结构能折衷一下数组和链表的优缺点呢?那就是——哈希表,既满足了数据的查找和修改很容易,同时又不占用很多空间的特点。

    哈希表是基于哈希函数的,哈希表中的元素是有哈希函数确定的,哈希表作为一种数据结构,我们用哈希表来存储数据,在保存的时候存入的是一个<key—value>的结构,value由哈希函数作用于key上得到。但是存在一个哈希冲突问题,那就是当你用hash函数作用在两个互不相同的key上,得到的value值相等。

    在runtime中,维护着一个sideTables,他是一个哈希表。


    image.png

    sideTables由多个SideTable组成。


    image.png
    sideTable又由一个自旋锁slock,一个引用计数表refcnts,一个弱引用表weak_table组成。

    Retain和Release

    1. Retain
    objc_object::rootRetain(bool tryRetain, bool handleOverflow)
    {
        if (isTaggedPointer()) return (id)this;
    
        bool sideTableLocked = false;
        bool transcribeToSideTable = false;
    
        isa_t oldisa;
        isa_t newisa;
    
        do {
            transcribeToSideTable = false;
            oldisa = LoadExclusive(&isa.bits);
            newisa = oldisa;
            if (slowpath(!newisa.nonpointer)) {
                ClearExclusive(&isa.bits);
                if (!tryRetain && sideTableLocked) sidetable_unlock();
                if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
                else return sidetable_retain();
            }
            // don't check newisa.fast_rr; we already called any RR overrides
            if (slowpath(tryRetain && newisa.deallocating)) {
                ClearExclusive(&isa.bits);
                if (!tryRetain && sideTableLocked) sidetable_unlock();
                return nil;
            }
            uintptr_t carry;
            newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc++
    
            if (slowpath(carry)) {
                // newisa.extra_rc++ overflowed
                if (!handleOverflow) {
                    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;
                newisa.extra_rc = RC_HALF;
                newisa.has_sidetable_rc = true;
            }
        } while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
    
        if (slowpath(transcribeToSideTable)) {
            // Copy the other half of the retain counts to the side table.
            sidetable_addExtraRC_nolock(RC_HALF);
        }
    
        if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
        return (id)this;
    }
    

    当调用retain时,isa.bits中的extra_rc+1
    当extra_rc溢出时,sideTable.refcnts+1。
    而之前提到的TaggedPointer是不进行引用计数的。

    1. retainCount方法


      image.png
      image.png

      返回extra_rc+1的值,如果sidetable_rc有值再加上sideTable中的值。

    3.release

    objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
    {
        if (isTaggedPointer()) return false;
    
        bool sideTableLocked = false;
    
        isa_t oldisa;
        isa_t newisa;
    
     retry:
        do {
            oldisa = LoadExclusive(&isa.bits);
            newisa = oldisa;
            if (slowpath(!newisa.nonpointer)) {
                ClearExclusive(&isa.bits);
                if (sideTableLocked) sidetable_unlock();
                return sidetable_release(performDealloc);
            }
            // don't check newisa.fast_rr; we already called any RR overrides
            uintptr_t carry;
            newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc--
            if (slowpath(carry)) {
                // don't ClearExclusive()
                goto underflow;
            }
        } while (slowpath(!StoreReleaseExclusive(&isa.bits, 
                                                 oldisa.bits, newisa.bits)));
    
        if (slowpath(sideTableLocked)) sidetable_unlock();
        return false;
    
     underflow:
        // newisa.extra_rc-- underflowed: borrow from side table or deallocate
    
        // abandon newisa to undo the decrement
        newisa = oldisa;
    
        if (slowpath(newisa.has_sidetable_rc)) {
            if (!handleUnderflow) {
                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.
                goto retry;
            }
    
            // Try to remove some retain counts from the side table.        
            size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);
    
            // To avoid races, has_sidetable_rc must remain set 
            // even if the side table count is now zero.
    
            if (borrowed > 0) {
                // Side table retain count decreased.
                // Try to add them to the inline count.
                newisa.extra_rc = borrowed - 1;  // redo the original decrement too
                bool stored = StoreReleaseExclusive(&isa.bits, 
                                                    oldisa.bits, newisa.bits);
                if (!stored) {
                    // Inline update failed. 
                    // Try it again right now. This prevents livelock on LL/SC 
                    // architectures where the side table access itself may have 
                    // dropped the reservation.
                    isa_t oldisa2 = LoadExclusive(&isa.bits);
                    isa_t newisa2 = oldisa2;
                    if (newisa2.nonpointer) {
                        uintptr_t overflow;
                        newisa2.bits = 
                            addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
                        if (!overflow) {
                            stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits, 
                                                           newisa2.bits);
                        }
                    }
                }
    
                if (!stored) {
                    // Inline update failed.
                    // Put the retains back in the side table.
                    sidetable_addExtraRC_nolock(borrowed);
                    goto retry;
                }
    
                // Decrement successful after borrowing from side table.
                // This decrement cannot be the deallocating decrement - the side 
                // table lock and has_sidetable_rc bit ensure that if everyone 
                // else tried to -release while we worked, the last one would block.
                sidetable_unlock();
                return false;
            }
            else {
                // Side table is empty after all. Fall-through to the dealloc path.
            }
        }
    
        // Really deallocate.
    
        if (slowpath(newisa.deallocating)) {
            ClearExclusive(&isa.bits);
            if (sideTableLocked) sidetable_unlock();
            return overrelease_error();
            // does not actually return
        }
        newisa.deallocating = true;
        if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
    
        if (slowpath(sideTableLocked)) sidetable_unlock();
    
        __sync_synchronize();
        if (performDealloc) {
            ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
        }
        return true;
    }
    

    extra_rc - 1
    当extra_rc下溢出时,查找sideTable。如果sideTable也没有时,对this发送SEL_dealloc方法进行析构。

    相关文章

      网友评论

          本文标题:iOS探究 --- 内存管理

          本文链接:https://www.haomeiwen.com/subject/zllqghtx.html