Android系統智能指針的設計思路(輕量級指針、強指針、弱指針)

本博客爲原創,轉載請註明出處,謝謝。android

參考博文:Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析 app

 

      C++中最容易出錯的地方莫過於指針了,指針問題主要有兩類,一是內存泄露,二是無效引用。new出來的對象忘記delete,形成這部份內存沒法使用沒法回收,引發內存泄露的問題;多個指針指向同一個對象,在一處delete以後,其餘指針在不知情的狀況下繼續引發訪問錯誤,甚至造成一個引起惡意攻擊的漏洞。less

      Android底層是由C++實現的,在指針和對象的管理上也下了很多的功夫,實現的智能指針也不只僅是多一個引用則引用數加一,移除一個引用引用數減一,爲0後刪除那麼簡單。能夠說Android的智能指針是一套體系,實現的至關精妙。函數

      不想看代碼的解析的能夠直接拉到最後看結論ui

      代碼選擇的4.0.1版本。this

 

      基本智能指針的實現:atom

      智能指針是經過引用技術實現指針指向的對象的共享,對象的建立和刪除(主要是刪除)交給智能指針處理,而不用用戶過度關心。spa

      實現智能指針須要兩步:一是爲指向的對象關聯引用計數,二是構造智能指針對象。.net

      引用計數是目標對象的屬性,實現方法是:編寫基類,實現引用計數的維護,而後讓指向的類繼承該基類,得到引用技術的屬性。該基類在Android系統中爲LightRefBase和RefBase,實如今frameworks\native\include\utils\RefBase.h中。指針

      智能指針自己是一個對象,負責維護引用計數並根據引用計數delete引用對象。

 

      Android系統中普通智能指針--輕量級指針

      輕量級指針基本智能指針的實現,經過一個計數器保持對象的引用。

template <class T>
class LightRefBase
{
public:
    inline LightRefBase() : mCount(0) { }
    inline void incStrong(const void* id) const {
        android_atomic_inc(&mCount);
    }
    inline void decStrong(const void* id) const {
        if (android_atomic_dec(&mCount) == 1) {
            delete static_cast<const T*>(this);
        }
    }
    //! DEBUGGING ONLY: Get current strong ref count.
    inline int32_t getStrongCount() const {
        return mCount;
    }

    typedef LightRefBase<T> basetype;

protected:
    inline ~LightRefBase() { }

private:
    friend class ReferenceMover;
    inline static void moveReferences(void* d, void const* s, size_t n,
            const ReferenceConverterBase& caster) { }

private:
    mutable volatile int32_t mCount;
};

      這個類中最重要的是 mutable volatile int32_t mCount; 這個類的任務就是維護mCount這個引用計數器, mutable實現互斥訪問,volatile保證值的最新,類提供增長和減小mCount的方法,當引用爲1而後再減小時則刪除對象。

      繼承於該類的對象的智能指針在構建新對象,執行拷貝構造函數以及析構函數時會調用其中的方法    

sp<T>::sp(T* other)  
    : m_ptr(other)  
{  
    if (other) other->incStrong(this);  
}  
  
sp<T>::sp(const sp<T>& other)  
    : m_ptr(other.m_ptr)  
{  
    if (m_ptr) m_ptr->incStrong(this);  
}  

sp<T>::~sp()  
{  
    if (m_ptr) m_ptr->decStrong(this);  
}  

     

      Android系統輕量級指針就是實現了基本的智能指針,比較容易理解。這種形式的智能指針很大程度上解決了C++指針問題,但以下場景還會力不從心。

     系統中有兩個對象A和B,在對象A的內部引用了對象B,而在對象B的內部也引用了對象A。當兩個對象A和B都再也不使用時,系統會發現沒法回收這兩個對象的所佔據的內存的,由於系統一次只能回收一個對象,而不管系統決定要收回對象A仍是要收回對象B時,都會發現這個對象被其它的對象所引用,於是就都回收不了,相似於死鎖現象,這樣就形成了內存泄漏。

     針對這個問題,能夠採用對象的引用計數同時存在強引用和弱引用兩種計數。例如A引用B則B的強引用計數和弱引用計數+1,而B引用A則A僅僅弱引用數+1,在回收時只要對象的強引用計數爲0,則無論弱引用數是否爲0都進行回收,相似於死鎖解決中的強制釋放資源,這樣問題獲得解決。

     Android中的強指針和弱指針

     如下有幾個概念容易混淆,先提出來辨析

     強引用計數和弱引用計數:目標對象關聯的兩個計數屬性,這兩個計數屬性同時存在,值由指針控制,不必定相等。

     強指針和弱指針:這是兩個不一樣的智能指針對象,在建立對象、拷貝構造以及析構的時候改變引用對象的強引用計數和弱引用計數,兩種指針的改變規則不一樣。

     對象採用的引用計數方法:用flag表示目標對象在回收時受哪一種強引用計數仍是弱引用計數影響,4.0版本沒有了forever模式。

 引用計數類代碼以下:

class RefBase
{
public:
            void            incStrong(const void* id) const;
            void            decStrong(const void* id) const;
    
            void            forceIncStrong(const void* id) const;

            //! DEBUGGING ONLY: Get current strong ref count.
            int32_t         getStrongCount() const;

    class weakref_type
    {
    public:
        RefBase*            refBase() const;
        
        void                incWeak(const void* id);
        void                decWeak(const void* id);
        
        // acquires a strong reference if there is already one.
        bool                attemptIncStrong(const void* id);
        
        // acquires a weak reference if there is already one.
        // This is not always safe. see ProcessState.cpp and BpBinder.cpp
        // for proper use.
        bool                attemptIncWeak(const void* id);

        //! DEBUGGING ONLY: Get current weak ref count.
        int32_t             getWeakCount() const;

        //! DEBUGGING ONLY: Print references held on object.
        void                printRefs() const;

        //! DEBUGGING ONLY: Enable tracking for this object.
        // enable -- enable/disable tracking
        // retain -- when tracking is enable, if true, then we save a stack trace
        //           for each reference and dereference; when retain == false, we
        //           match up references and dereferences and keep only the 
        //           outstanding ones.
        
        void                trackMe(bool enable, bool retain);
    };
    
            weakref_type*   createWeak(const void* id) const;
            
            weakref_type*   getWeakRefs() const;

            //! DEBUGGING ONLY: Print references held on object.
    inline  void            printRefs() const { getWeakRefs()->printRefs(); }

            //! DEBUGGING ONLY: Enable tracking of object.
    inline  void            trackMe(bool enable, bool retain)
    { 
        getWeakRefs()->trackMe(enable, retain); 
    }

    typedef RefBase basetype;

protected:
                            RefBase();
    virtual                 ~RefBase();

    //! Flags for extendObjectLifetime()
    enum {
        OBJECT_LIFETIME_STRONG  = 0x0000,
        OBJECT_LIFETIME_WEAK    = 0x0001,
        OBJECT_LIFETIME_MASK    = 0x0001
    };
    
            void            extendObjectLifetime(int32_t mode);
            
    //! Flags for onIncStrongAttempted()
    enum {
        FIRST_INC_STRONG = 0x0001
    };
    
    virtual void            onFirstRef();
    virtual void            onLastStrongRef(const void* id);
    virtual bool            onIncStrongAttempted(uint32_t flags, const void* id);
    virtual void            onLastWeakRef(const void* id);

private:
    friend class ReferenceMover;
    static void moveReferences(void* d, void const* s, size_t n,
            const ReferenceConverterBase& caster);

private:
    friend class weakref_type;
    class weakref_impl;
    
                            RefBase(const RefBase& o);
            RefBase&        operator=(const RefBase& o);

        weakref_impl* const mRefs;
};

      和輕量級指針LightRefBase相比,RefBase最重要的區別是引用計數變成了weakref_impl* const mRefs

class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
    volatile int32_t    mStrong;
    volatile int32_t    mWeak;
    RefBase* const      mBase;
    volatile int32_t    mFlags;

    weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE)
        , mWeak(0)
        , mBase(base)
        , mFlags(0)
    {
    }
}

      能夠看到weakref_impl包含了四個屬性字段,而LightRefBase中只有mCount,其中mStrong是強引用計數, mWeak是弱引用計數, mBase指向宿主類, mFlags標識對象受哪一種計數方式影響。

     其中mFlags可選的值是 OBJECT_LIFETIME_WEAK  = 0x0000 :表示受強引用計數影響; OBJECT_LIFETIME_WEAK = 0x0001:受弱引用計數影響; OBJECT_LIFETIME_MASK = 0x0001:這個還沒太懂

     mFlags默認值爲0,經過 extendObjectLifetime(int32_t mode) 函數改變其值。

     這個類中其餘重要的幾個函數就是 incStrong, decStrong, incWeak 和 decWeak了,其中incStrong並比是簡單的把mStrong的值加1,這幾個計數的增長和減小都有一系列的規則。

incStrong函數:

void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->incWeak(id);
    
    refs->addStrongRef(id);
    const int32_t c = android_atomic_inc(&refs->mStrong);
    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
#if PRINT_REFS
    ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
    if (c != INITIAL_STRONG_VALUE)  {
        return;
    }

    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
    refs->mBase->onFirstRef();
}

      能夠看到incStrong函數中作了三件事,1:增長弱引用計數;2:增長強引用計數;3:若是是第一次調用,則調用onFirstRef()

      強弱智能指針最重要的是在減小引用計數時的操做。

decStrong:

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);
    const int32_t c = android_atomic_dec(&refs->mStrong);
#if PRINT_REFS
    ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
    ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
    if (c == 1) {
        refs->mBase->onLastStrongRef(id);
        if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
            delete this;
        }
    }
    refs->decWeak(id);
}

decWeak:

void RefBase::weakref_type::decWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->removeWeakRef(id);
    const int32_t c = android_atomic_dec(&impl->mWeak);
    ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
    if (c != 1) return;

    if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
        // This is the regular lifetime case. The object is destroyed
        // when the last strong reference goes away. Since weakref_impl
        // outlive the object, it is not destroyed in the dtor, and
        // we'll have to do it here.
        if (impl->mStrong == INITIAL_STRONG_VALUE) {
            // Special case: we never had a strong reference, so we need to
            // destroy the object now.
            delete impl->mBase;
        } else {
            // ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
            delete impl;
        }
    } else {
        // less common case: lifetime is OBJECT_LIFETIME_{WEAK|FOREVER}
        impl->mBase->onLastWeakRef(id);
        if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
            // this is the OBJECT_LIFETIME_WEAK case. The last weak-reference
            // is gone, we can destroy the object.
            delete impl->mBase;
        }
    }
}

        將強引用計數減1,onLastStrongRef函數爲空,不用考慮。

   當減小強引用計數前爲1,即如今強引用計數爲0,則執行if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG),其中OBJECT_LIFETIME_MASK值爲0x0001,和WEAK的相同。條件在只有在mFlags爲0時成立。這時候說明對象受強引用計數影響,全部刪除對象。

       refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK。相似條件式子的理解。此式子在mFlags爲0的時候爲真,mFlags爲OBJECT_LIFETIME_WEAK或者OBJECT_LIFETIME_FOREVER是爲假,代碼中有很多相似式子,爲何沒有直接判斷而是用這樣的複雜式子我理解是由於表達的一致性和代碼的可讀性。

      在decWeak函數中,若是調用到這裏,那麼就說明前面必定有增長過此對象的弱引用計數,而增長對象的弱引用計數有兩種場景的,一種場景是增長對象的強引用計數的時候,會同時增長對象的弱引用計數,另外一種場景是當咱們使用一個弱指針來指向對象時,在弱指針對象的構造函數裏面,也會增長對象的弱引用計數,不過這時候,就只是增長對象的弱引用計數了,並無同時增長對象的強引用計數。所以,這裏在減小對象的弱引用計數時,就要分兩種狀況來考慮。

      若是是前一種場景,這裏的impl->mStrong就必然等於0,而不會等於INITIAL_STRONG_VALUE值,所以,這裏就不須要delete目標對象了(impl->mBase),由於前面的RefBase::decStrong函數會負責delete這個對象。這裏惟一須要作的就是把weakref_impl對象delete掉。

      若是是後一種情景,這裏的impl->mStrong值就等於INITIAL_STRONG_VALUE了,這時候因爲沒有地方會負責delete目標對象,所以,就須要把目標對象(imp->mBase)delete掉了,不然就會形成內存泄漏。在delete這個目標對象的時候,就會執行RefBase類的析構函數,這時候目標對象的弱引用計數等於0,因而,就會把weakref_impl對象也一塊兒delete掉了。

 

      到目前爲止,討論的是強引用計數和弱引用計數在增長和減小時的狀況,規則見最後的結論。

   

      強指針sp和弱指針wp

     強指針sp定義在frameworks\native\include\utils\StrongPointer.h

     弱指針按期在frameworks\native\include\utils\RefBase.h

     與強指針類相比,它們都有一個成員變量m_ptr指向目標對象,可是弱指針還有一個額外的成員變量m_refs,它的類型是weakref_type指針。

     強指針直接調用incStrong和decStrong,會同時增長或減小強引用計數和弱引用計數。

     弱指針調用createWeak進而調用incWeak,或者調用decWeak,隻影響弱引用計數,不影響強引用計數。

     弱指針的最大特色是它不能直接操做目標對象,這是怎麼樣作到的呢?祕密就在於弱指針類沒有重載*和->操做符號,而強指針重載了這兩個操做符號。可是,若是咱們要操做目標對象,應該怎麼辦呢,這就要把弱指針升級爲強指針了。

     弱指針升級爲強指針經過promote函數實現

sp<T> wp<T>::promote() const
{
    sp<T> result;
    if (m_ptr && m_refs->attemptIncStrong(&result)) {
        result.set_pointer(m_ptr);
    }
    return result;
}

    若是m_ptr不爲空而且attemptIncStrong成功,則經過弱指針的m_ptr構建強指針。

bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
    incWeak(id);
    
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    
    int32_t curCount = impl->mStrong;
    ALOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow",
               this);
    while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
        if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
            break;
        }
        curCount = impl->mStrong;
    }
    
    if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
        bool allow;
        if (curCount == INITIAL_STRONG_VALUE) {
            // Attempting to acquire first strong reference...  this is allowed
            // if the object does NOT have a longer lifetime (meaning the
            // implementation doesn't need to see this), or if the implementation
            // allows it to happen.
            allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK
                  || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
        } else {
            // Attempting to revive the object...  this is allowed
            // if the object DOES have a longer lifetime (so we can safely
            // call the object with only a weak ref) and the implementation
            // allows it to happen.
            allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK
                  && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
        }
        if (!allow) {
            decWeak(id);
            return false;
        }
        curCount = android_atomic_inc(&impl->mStrong);

        // If the strong reference count has already been incremented by
        // someone else, the implementor of onIncStrongAttempted() is holding
        // an unneeded reference.  So call onLastStrongRef() here to remove it.
        // (No, this is not pretty.)  Note that we MUST NOT do this if we
        // are in fact acquiring the first reference.
        if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {
            impl->mBase->onLastStrongRef(id);
        }
    }
    
    impl->addStrongRef(id);

#if PRINT_REFS
    ALOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount);
#endif

    if (curCount == INITIAL_STRONG_VALUE) {
        android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);
        impl->mBase->onFirstRef();
    }
    
    return true;
}

 

 

總結:

      1. Android中LightRefBase實現了基本的智能指針功能。

      2. 強指針和弱指針是爲了解決循環引用中類死鎖問題引發的內存泄露。

      3. 引用對象繼承RefBase,則擁有了強引用計數和弱引用計數的屬性。

      4. 經過extendObjectLifetime函數改變mFlags的值,肯定對象受哪一種引用計數影響。

      5. 調用incStrong增長強引用計數時,會同時增長強引用計數和弱引用計數,第一次增長強引用計數時,強引用計數從INITIAL_STRONG_VALUE改變爲1。

      6. 調用incWeak增長弱引用計數時,只有弱引用計數增長,強引用計數不變。

      7. 弱引用計數≥強引用計數,強增弱必增,弱加強不必定增。

      8. 調用decStrong減小強引用指針時:

          若是對象受強引用計數影響,強引用計數減1,若是此時強引用計數爲0,無論弱引用計數是否爲0,刪除引用對象(此時因爲弱引用計數還沒減小,因此析構函數不會刪除指針對象)。而後弱引用計數減1,若是此時弱引用計數也爲0,則刪除指針對象。

          若是對象受弱引用計數影響,強引用計數減1,弱引用計數減1,若是此時弱引用計數爲0,則刪除引用對象,在引用對象的析構函數中會刪除指針對象。

      9. 調用decWeak減小弱引用指針時:

          若是對象受強引用計數影響,弱引用計數減1,若是此時弱引用計數爲0,看看強引用計數,若是爲INITIAL_STRONG_VALUE則說明沒有強指針指向該對象,此時直接刪除引用對象,指針對象也會隨之刪除。若是強引用計數不爲INITIAL_STRONG_VALUE,則必定爲0,由於強引用計數小於等於弱引用計數,此時只須要刪除指針對象,由於強引用計數爲0而且對象受強引用計數影響,因此對象已經被刪除。

          若是對象受弱引用計數影響,弱引用計數減1,若是此時弱引用計數爲0,則刪除引用對象,指針對象也隨之刪除。

      10. 強指針和弱指針是兩個不一樣的類,引用對象時會以不一樣的方式改變強引用計數和弱引用計數的值。

      11. 強指針直接調用incStrong和decStrong,會同時增長或減小強引用計數和弱引用計數。弱指針調用createWeak進而調用incWeak,或者調用decWeak,隻影響弱引用計數,不影響強引用計數。

      12. 弱指針不能直接操做目標對象,由於弱指針類沒有重載*和->操做符號,而強指針重載了這兩個操做符號。若是咱們要操做目標對象,就要使用promote函數把弱指針升級爲強指針。

      13. 弱指針轉化爲強指針不必定成功,若是能夠轉化則使用弱指針的m_ptr構造強指針。

      14. 智能指針方案實現的很精妙,建議讀源碼去體會,我參考的4.0的源碼註釋也比較清晰,能幫助理解。

相關文章
相關標籤/搜索