Android系統的計數基類LightRefBase、RefBase與智能指針sp、wp

Android系統的計數基類LightRefBase、RefBase與智能指針sp、wp

導讀android

Android系統的運行時庫層代碼是用C++語言來編寫的,使用用C++來寫代碼最容易出錯的地方就是指針了,一旦使用不當,輕則形成內存泄漏,重則形成系統崩潰。所以C++程序造成了一個常識:一個new配一個delete,一個malloc配一個free。new的時機很是好判斷,當使用者須要新的對象的時候直接new便可。delete的時機有些難判斷,有時候你不知道是否還有指針指向這個對象,當前你是否可以delete,亦或是你可能忘記了delete這個對象,冒然的進行delete可能會給系統帶來不少野指針。ios

爲了不出現上述問題,通常的作法就是使用引用計數的方法,每當有一個指針指向了一個new出來的對象時,就對這個對象的引用計數增長1,每當有一個指針再也不使用這個對象時,就對這個對象的引用計數減小1,每次減1以後,若是發現引用計數值爲0時,那麼,就要delete這個對象了,這樣就避免了忘記delete對象或者這個對象被delete以後其它地方還在使用的問題了。面試

可是,如何實現這個對象的引用計數呢?安全

針對這麼容易出錯也比較棘手的問題,Android系統爲咱們提供了計數基類和智能指針,避免出現上述問題,本文將系統地分析Android系統智能指針(計數基類-RefBase、強指針-sp和弱指針-wp)的實現原理。app

##1.輕量級計數基類-LightRefBase 首先來看一下系統提供的輕量級計數基類LightRefBase模板,以下代碼1所示:框架

[代碼1 - LightRefBase源碼]less

template <class T>
class LightRefBase
{
public:
    inline LightRefBase() : mCount(0) { }
    inline void incStrong(__attribute__((unused)) const void* id) const {
        android_atomic_inc(&mCount);
    }
    inline void decStrong(__attribute__((unused)) 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 renameRefs(size_t n, const ReferenceRenamer& renamer) { }
    inline static void renameRefId(T* ref,
            const void* old_id, const void* new_id) { }

private:
    mutable volatile int32_t mCount;
};

在代碼1中,LightRefBase做爲全部類的基類,相似於Java中的Object,經過關鍵變量mCount,實現全部類對象引用關係的計數。這裏經過一個C++例子來看,筆者這裏使用了Eclipse IDE for C/C++ Developers,將android_atomic_inc(&mCount)依賴於系統給操做變成了++mCount。代碼以下所示:函數

[代碼2 - 父類使用LightRefBase引用計數功能]測試

#include "utils/RefBase.h"
#include "utils/StrongPointer.h"
#include <iostream>
using namespace std;

class A: public LightRefBase<A> {
public:
	A() {
		cout << "Construct object" << endl;
	}
	virtual ~A() {
		cout << "Destory object" << endl;
	}
};

int main(int argc, char** argv) {
	A* pa = new A();
	cout << "0. Light Ref Count: " << pa->getStrongCount() << endl;
	// 引用+1
	sp<A> lpOut = pa;
	cout << "1. Light Ref Count: " << pa->getStrongCount() << endl;
	{
		// 引用+2
		sp<A> lpInner = lpOut;
		cout << "2. Light Ref Count: " << pa->getStrongCount() << endl;
	}
	cout << "3. Light Ref Count: " << pa->getStrongCount() << endl;
	return 0;
}

代碼輸出結果:ui

Construct object
0. Light Ref Count: 0
1. Light lpOut Count: 1
2. Light Ref Count: 2
Destory object
3. Light Ref Count: 6357188

代碼2中,類A做爲LightRefBase類的父類,使用了LightRefBase類模板,經過父類A實例化,將關鍵變量mCount設爲初值0。這裏又藉助了sp,sp又是什麼呢?sp(StrongPointer)也是一個類模版,目前LightRefBase類已經提供了計數變量,可是自身是沒法完成計數工做的,須要藉助sp來完成計數的工做。經過sp告知當前實際對象的mCount變量增長或者減小。在代碼2中,增長做用域,經過sp<A> lpInner = lpOut繼續增長mCount變量。

###1.1 sp告知計數增長 在代碼2中,能夠看到sp<A> lpOut = pa;代碼令mCount變量加了1,這個是如何來完成的呢?參考sp中的代碼以下:

template<typename T>
sp<T>::sp(T* other) :
	m_ptr(other) {
	if (other)
		other->incStrong(this);
}

經過m_ptr指針指向pa指向的實際對象,other指針調用實際對象mCount變量加1。經過代碼1中的cout << "1. Light Ref Count: " << pa->getStrongCount() << endl代碼輸出mCount的值等於1。這裏來看一下實際對象的基類LightRefBase中的incStrong方法。在代碼1中能夠看到,很是簡單經過調用android_atomic_inc完成mCount的值自增1。

###1.2 sp告知計數減小 在代碼2的輸出中,能夠看到跳出做用域的時候,A對象被銷燬,輸出Destory object,sp的析構函數~sp()也被調用,這裏來看一下sp告知計數減小:

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

經過m_ptr調用decStrong完成mCount減小操做。這裏來看一下實際對象的基類LightRefBase中的decStrong方法。在代碼1中能夠看到,很是簡單,首先經過經過判斷android_atomic_dec對mCount作本身操做,若是發現減1前已經等於1,也就是沒有了額外引用經過delete銷燬對象。

###1.3 小結

代碼1流程示意圖

圖1-1 代碼1流程示意圖

在上面介紹的過程當中能夠獲得以下結論:

  1. 不經過sp指向指針,mCount值不變,經過sp<T>定義指針mCount值加一。
  2. 指針之間指向地址相同的地址空間,之間不存在關聯性,當實例對象還在的時候任何一個指針(pa、lpOut、lpInner)均可以拿到mCount的值。
  3. sp析構函數被調用,mCount值等於1的時候,實例對象會被銷燬,其他指針會變成野指針,參考代碼1輸出。

##2.複雜型計數基類-RefBase

在計算機科學領域中,垃圾收集(Garbage Collection)功能的系統框架,即提供對象託管功能的系統框架,例如Java應用程序框架,也是採用上述的引用計數技術方案來實現的,然而,簡單的引用計數技術不能處理系統中對象間循環引用的狀況。考慮這樣的一個場景,系統中有兩個對象A和B,在對象A的內部引用了對象B,而在對象B的內部也引用了對象A。當兩個對象A和B都再也不使用時,垃圾收集系統會發現沒法回收這兩個對象的所佔據的內存的,由於系統一次只能收集一個對象,而不管系統決定要收回對象A仍是要收回對象B時,都會發現這個對象被其它的對象所引用,於是就都回收不了,這樣就形成了內存泄漏。這樣,就要採起另外的一種引用計數技術了,即對象的引用計數同時存在強引用和弱引用兩種計數,例如,Apple公司提出的Cocoa框架,當父對象要引用子對象時,就對子對象使用強引用計數技術,而當子對象要引用父對象時,就對父對象使用弱引用計數技術,而當垃圾收集系統執行對象回收工做時,只要發現對象的強引用計數爲0,而無論它的弱引用計數是否爲0,均可以回收這個對象,可是,若是咱們只對一個對象持有弱引用計數,當咱們要使用這個對象時,就不直接使用了,必需要把這個弱引用升級成爲強引用時,才能使用這個對象,在轉換的過程當中,若是對象已經不存在,那麼轉換就失敗了,這時候就說明這個對象已經被銷燬了,不能再使用了。

RefBase要比LightRefBase類要複雜的多,當上述輕量級基類LightRefBase沒法知足需求的時候,就須要計數基類RefBase。RefBase在源碼中的位置爲system/core/include/utils/RefBase.h文件中,代碼以下所示:

[代碼3 - 複雜型計數基類RefBase類]

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 weakref_type;
    class weakref_impl;
    
                            RefBase(const RefBase& o);
            RefBase&        operator=(const RefBase& o);

private:
    friend class ReferenceMover;

    static void renameRefs(size_t n, const ReferenceRenamer& renamer);

    static void renameRefId(weakref_type* ref,
            const void* old_id, const void* new_id);

    static void renameRefId(RefBase* ref,
            const void* old_id, const void* new_id);

        weakref_impl* const mRefs;
};

RefBase類和LightRefBase類同樣,都提供了incStrong和decStrong成員函數來操做它的引用計數器。可是在代碼3中確找不到mutable volatile int32_t mCount變量來維護引用計數。RefBase提供了強、弱計數機制,經過weakref_impl* const mRefs來提供,具體咱們來看一下,強、弱計數是如何提供的。

RefBase類的成員變量mRefs的類型爲weakref_impl指針,實現是在system/core/libutils/RefBase.cpp文件中實現的,以下代碼4所示:

[代碼4 - weakref_impl類]

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

#if !DEBUG_REFS

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

    void addStrongRef(const void* /*id*/) { }
    void removeStrongRef(const void* /*id*/) { }
    void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) { }
    void addWeakRef(const void* /*id*/) { }
    void removeWeakRef(const void* /*id*/) { }
    void renameWeakRefId(const void* /*old_id*/, const void* /*new_id*/) { }
    void printRefs() const { }
    void trackMe(bool, bool) { }

#else

// Debug版本的源代碼
.....

#endif
};

爲了查看方便,這裏咱們去掉Debug版本的源代碼,若是讀者感興趣能夠自行閱讀。在代碼4中weakref_impl主要提供了四個公有變量,用來維護對象的引用計數。值得關注的主要有以下四點:

  • mStrong表示對象強引用計數,mWeak表示對象弱引用計數。
  • RefBase類中包含weakref_impl類指針mRefs。
  • 同時weakref_impl類也提供了一個成員變量mBase來指向宿主RefBase。
  • mFlags是一個標誌位,它指示了維護對象引用計數所使用的策略,它的取值爲0。

這裏還須要注意一點weakref_impl繼承RefBase::weakref_type類,做爲一個實現類出現,weakref_type必然會做爲接口類,這個對應的接口類的就是RefBase類內部定義的weakref_type類了,這是一種把類的實現與接口定義分離的設計方法。在代碼3中,能夠看到。RefBase類看起來仍是很複雜的,接下來咱們畫個簡單的示意圖來看一下RefBase的關係:

RefBase實現類圖

圖2 RefBase實現類圖

上圖中能夠看出weakref_type做爲RefBase的內部類(聲明RefBase.h文件中),weakref_impl實現了RefBase::weakref_type(實如今RefBase.cpp文件中)。

提供引用計數器的類RefBase就暫時介紹到這裏,這裏咱們也和LightRefBase基類同樣,結合智能指針sp進行分析,稍候經過一個例子來講明覆雜型計數基類RefBase的計數功能,但這要比LightRefBase複雜的多。這裏咱們用一節內容去描述強指針sp和計數基類RefBase配合實現。

##3.強指針-sp

複雜型計數基類RefBase做爲全部類的基類,使用sp<T>聲明強指針的時候,調用sp模板的構造函數,咱們來看一下sp的構造函數實現:

template<typename T>
sp<T>::sp(T* other) :
	m_ptr(other) {
	if (other)
		other->incStrong(this);
}

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

這裏傳入參數other必定是繼承了RefBase類,other調用RefBase的incStrong函數。在圖2中的類關係圖能夠看出,incStrong函數實如今system/core/libutils/RefBase.cpp文件中。代碼以下所示:

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();
}

成員變量mRefs是在RefBase類的構造函數中建立的,類型爲weakref_impl,做爲ReBase的關鍵變量,前面都已經介紹過。在incStrong完成以下工做:

  1. refs->incWeak增長弱引用計數,函數內部調用impl->addWeakRef(id)android_atomic_inc(&impl->mWeak)(其中impl類型爲weakref_impl)增長弱引用計數變量mWeak
  2. refs->addStrongRef增長強引用計數,android_atomic_inc(&refs->mStrong)增長強引用計數變量mStrong的值。
  3. 若是發現是首次調用這個對象的incStrong函數,就會調用一個這個對象的onFirstRef函數,讓對象有機會在對象被首次引用時作一些處理邏輯。

第三點中c返回的是refs->mStrong加1前的值,若是發現值等於INITIAL_STRONG_VALUE,就說明這個對象的強引用計數是第一次被增長,#define INITIAL_STRONG_VALUE (1<<28),說明refs->mStrong的默認初值是INITIAL_STRONG_VALUE。其中咱們須要注意的是android_atomic_inc返回值c爲操做前的結果。

android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong)中refs->mStrong這個值加1後等於1<<28 + 1,不等於1,所以,後面要再減去-INITIAL_STRONG_VALUE,因而,refs->mStrong就等於1了,就表示當前對象的強引用計數值爲1了,這與這個對象是第一次被增長強引用計數值的邏輯是一致的。

第一點中咱們來看一下弱引用計數是如何增長的,這裏咱們看一下refs->incWeak(id)函數。咱們知道,在Release版本中,impl->addWeakRef函數什麼也不作,而在Debug版本中,這個函數增長了一個ref_entry對象到了weakref_impl對象的mWeakRefs列表中,表示此weakref_impl對象的弱引用計數被增長了一次。

void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->addWeakRef(id);
    const int32_t c __unused = android_atomic_inc(&impl->mWeak);
    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}

impl->addWeakRef(id)在老羅分析的版本中調用了兩次,老羅對此也產生了疑惑,目前6.0版本只調用了一次

這裏總結一下強指針類sp在其構造函數裏面所作的事情就是分別爲目標對象的強引用計數和弱引和計數增長了1。

再來看看強引用指針的析構函數的實現:

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

一樣,這裏的m_ptr指向的目標對象必定是繼承了RefBase類的,所以,這裏調用的是RefBase類的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);

    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);
}
  1. 這裏的refs->removeStrongRef函數調用語句是對應前面在RefBase::incStrong函數裏的refs->addStrongRef函數調用語句的,在Release版本中,這也是一個空實現函數,真正實現強引用計數減1的操做是android_atomic_dec(&refs->mStrong),若是發現減1前,此對象的強引用計數refs->mStrong爲1,就說明今後之後,就再沒有地方引用這個目標對象了,這時候,就要看看是否要delete這個目標對象了。

  2. 在強引用計數爲0的狀況下,若是對象的標誌位OBJECT_LIFETIME_MASK被設置了,就說明這個對象的生命週期是受弱引用計數所控制保護的,所以,這時候就不能delete對象,要等到弱引用計數也爲0的狀況下,才能delete這個對象。

  3. 接下來調用refs->decWeak(id)函數,和前面介紹的refs->incWeak(id)函數相對應,代碼以下,調用impl->removeWeakRef(id)它的做用和impl->addWeakRef(id)一致,只有在Debug版本中生效,Release版本中是一個空實現函數。

在第二點中對已經設置OBJECT_LIFETIME_MASK標誌位的對象進行了保護,直至弱引用計數變成0的的時候,那麼refs->decWeak(id)函數不只僅要對弱引用變量進行減法操做,還須要負責銷燬沒有銷燬的對象,代碼以下所示:

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;
        }
    }
}

android_atomic_dec函數對mWeak變量進行操做,對減1以前的值c進行判斷,若是等於1,開始進入對象回收。首先判斷(impl->mFlags&OBJECT_LIFETIME_WEAK)是否等於OBJECT_LIFETIME_STRONG,其中OBJECT_LIFETIME_WEAK和OBJECT_LIFETIME_MASK值是相同的,若是decStrong函數中由於設置了弱引用保護沒有delete對象,那麼此時這個條件依然不知足,執行else語句,釋放對象經過delete impl->mBase實現對象的銷燬。mBase是在每次實例化weakref_impl的時候傳入的RefBase對象。

enum {
    OBJECT_LIFETIME_STRONG  = 0x0000,
    OBJECT_LIFETIME_WEAK    = 0x0001,
    OBJECT_LIFETIME_MASK    = 0x0001
};

若是對象沒有設置弱引用保護,不受弱引用計數控制,首先判斷對象是否沒有過一次強計數引用,調用delete impl->mBase釋放對象,一般狀況impl->mStrong就必然等於0,而不會等於INITIAL_STRONG_VALUE值,除非它一次強引用都沒有過。若是impl->mStrong==0就不須要在delete這個對象了,由於前面已經將這個對象delete掉了,重複的操做每每會引發異常。惟一要作的就是把weakref_impl對象delete掉。爲何要delete這個weakref_impl對象呢?這裏的weakref_impl對象是在RefBase的構造函數裏面new出來的,理論上說應該在在RefBase的析構函數裏delete掉這個weakref_impl對象的。在RefBase的析構函數裏面,的確是會作這件事情:

RefBase::~RefBase()
{
    if (mRefs->mStrong == INITIAL_STRONG_VALUE) {
        // we never acquired a strong (and/or weak) reference on this object.
        delete mRefs;
    } else {
        // life-time of this object is extended to WEAK or FOREVER, in
        // which case weakref_impl doesn't out-live the object and we
        // can free it now.
        if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {
            // It's possible that the weak count is not 0 if the object
            // re-acquired a weak reference in its destructor
            if (mRefs->mWeak == 0) {
                delete mRefs;
            }
        }
    }
    // for debugging purposes, clear this.
    const_cast<weakref_impl*&>(mRefs) = NULL;
}

可是不要忘記,在這個場景下,目標對象是前面的RefBase::decStrong函數delete掉的,這時候目標對象就會被析構,可是它的弱引用計數值還沒有執行減1操做,所以,這裏的mRefs->mWeak == 0條件就不成立,因而就不會delete這個weakref_impl對象,所以,就延遲到執行這裏decWeak函數時再執行。

若是對象沒有過一次強計數引用,也就出現mRefs->mStrong == INITIAL_STRONG_VALUE狀況,RefBase析構函數依然會調用delete mRefs刪除相應的weakref_impl對象。在else首先判斷了mRefs->mFlags若是沒有設置了弱引用保護就再也不delete這個weakref_impl對象,由於在上面RefBase::weakref_type::decWeak中,咱們已經delete這個weakref_impl對象了。只有設置了弱引用保護mRefs->mFlags & OBJECT_LIFETIME_MASK等於1,不等於OBJECT_LIFETIME_STRONG的時候,咱們單獨delete這個weakref_impl對象。

OBJECT_LIFETIME_FOREVER設置在6.0上再也不提供,智能指針再也不可以退化成普通指針

目前主要分爲兩種狀況,第一種當不作弱引用保護的時候,強引用計數值爲0,則銷燬相應的對象;第二種看成弱引用保護的時候,目標對象的強引用計數和弱引用計數都同時爲0時,delete這個目標對象。weakref_impl對象是在弱引用計數爲0的時候,delete這個weakref_impl對象。

分析到這裏,有必要小結一下:

  1. 若是對象的標誌位被設置爲0,那麼只要發現對象的強引用計數值爲0,那就會自動delete掉這個對象;
  2. 若是對象的標誌位被設置爲OBJECT_LIFETIME_WEAK,那麼只有當對象的強引用計數和弱引用計數都爲0的時候,纔會自動delete掉這個對象;
  3. 只有弱引用計數爲0的時候,纔會deleteweakref_impl對象,可是標誌位被設置爲0,是在RefBase::weakref_type::decWeak函數中delete,若是標誌位被設置爲OBJECT_LIFETIME_WEAK是在RefBase析構函數中delete。

##4.弱指針-wp 弱指針所使用的引用計數類與強指針同樣,都是RefBase類,所以,這裏就再也不重複介紹了,咱們直接來弱指針的實現:

[代碼5 - wp模板]

template <typename T>
class wp
{
public:
    typedef typename RefBase::weakref_type weakref_type;
    
    inline wp() : m_ptr(0) { }

    wp(T* other);
    wp(const wp<T>& other);
    wp(const sp<T>& other);
    template<typename U> wp(U* other);
    template<typename U> wp(const sp<U>& other);
    template<typename U> wp(const wp<U>& other);

    ~wp();
    
    ......

private:
    template<typename Y> friend class sp;
    template<typename Y> friend class wp;

    T*              m_ptr;
    weakref_type*   m_refs;
};

與強指針類相比,它們都有一個成員變量m_ptr指向目標對象,可是弱指針還有一個額外的成員變量m_refs,它的類型是weakref_type指針,下面咱們分析弱指針的構造函數時再看看它是若是初始化的。這裏咱們須要關注的仍然是弱指針的構造函數和析構函數。

//構造函數
template<typename T>
wp<T>::wp(T* other)
    : m_ptr(other)
{
    if (other) m_refs = other->createWeak(this);
}
// 析構函數
template<typename T>
wp<T>::~wp()
{
    if (m_ptr) m_refs->decWeak(this);
}

構造函數和析構函數分別調用RefBase中的createWeakweakref_impl.decWeak函數:

RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
    mRefs->incWeak(id);
    return mRefs;
}

這裏的成員變量mRefs的類型爲weakref_impl指針,weakref_impl類的incWeak函數咱們在前面已經看過了,它的做用就是增長對象的弱引用計數。在析構函數中,弱指針對象的成員變量m_refs就指向目標對象的weakref_impl對象,最後調用decWeak()函數,它的做用是減小對象的弱引用計數。前面都已經作了介紹,這裏再也不重複。

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

template<typename T>
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和m_refs來構造一個強指針sp,這裏的m_ptr爲指目標對象的一個指針,而m_refs則是指向目標對象裏面的weakref_impl對象。這裏主要分紅兩個部分:

  1. 判斷目標對象是否爲空,而後m_refs->attemptIncStrong(&result)生成強引用。
  2. 經過result.set_pointer(m_ptr)將強指針指向目標對象,最後生成的強指針result返回。

首先根據第一點,看一下attemptIncStrong(&result)實現代碼:

[代碼6 - 弱指針轉爲強指針]

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) {
        // we're in the easy/common case of promoting a weak-reference
        // from an existing strong reference.
        if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
            break;
        }
        // the strong count has changed on us, we need to re-assert our
        // situation.
        curCount = impl->mStrong;
    }
    
    if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
        // we're now in the harder case of either:
        // - there never was a strong reference on us
        // - or, all strong references have been released
        if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
            // this object has a "normal" life-time, i.e.: it gets destroyed
            // when the last strong reference goes away
            if (curCount <= 0) {
                // the last strong-reference got released, the object cannot
                // be revived.
                decWeak(id);
                return false;
            }

            // here, curCount == INITIAL_STRONG_VALUE, which means
            // there never was a strong-reference, so we can try to
            // promote this object; we need to do that atomically.
            while (curCount > 0) {
                if (android_atomic_cmpxchg(curCount, curCount + 1,
                        &impl->mStrong) == 0) {
                    break;
                }
                // the strong count has changed on us, we need to re-assert our
                // situation (e.g.: another thread has inc/decStrong'ed us)
                curCount = impl->mStrong;
            }

            if (curCount <= 0) {
                // promote() failed, some other thread destroyed us in the
                // meantime (i.e.: strong count reached zero).
                decWeak(id);
                return false;
            }
        } else {
            // this object has an "extended" life-time, i.e.: it can be
            // revived from a weak-reference only.
            // Ask the object's implementation if it agrees to be revived
            if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {
                // it didn't so give-up.
                decWeak(id);
                return false;
            }
            // grab a strong-reference, which is always safe due to the
            // extended life-time.
            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);

    // now we need to fix-up the count if it was INITIAL_STRONG_VALUE
    // this must be done safely, i.e.: handle the case where several threads
    // were here in attemptIncStrong().
    curCount = impl->mStrong;
    while (curCount >= INITIAL_STRONG_VALUE) {
        ALOG_ASSERT(curCount > INITIAL_STRONG_VALUE,
                "attemptIncStrong in %p underflowed to INITIAL_STRONG_VALUE",
                this);
        if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE,
                &impl->mStrong) == 0) {
            break;
        }
        // the strong-count changed on us, we need to re-assert the situation,
        // for e.g.: it's possible the fix-up happened in another thread.
        curCount = impl->mStrong;
    }

    return true;
}

前面咱們在討論強指針的時候說到,增長目標對象的強引用計數的同時,也會增長目標對象的弱引用計數,所以,函數在開始的地方首先就是調用incWeak函數來先增長目標對象的引用計數,若是後面試圖增長目標對象的強引用計數失敗時,會調用decWeak函數來回滾前面的incWeak操做。

這裏試圖增長目標對象的強引用計數時,分兩種狀況討論:

  • 一種狀況是此時目標對象正在被其它強指針引用,即它的強引用計數(impl->mStrong)大於0,而且不等於INITIAL_STRONG_VALUE
  • 另外一種狀況是此時目標對象沒有被任何強指針引用,即它的強引用計數(impl->mStrong)小於等於0,或者等於INITIAL_STRONG_VALUE。

第一種狀況比較簡單,由於這時候說明目標對象必定存在,所以,是能夠將這個弱指針提高爲強指針的,在這種狀況下,只要簡單地增長目標對象的強引用計數值就行android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE,&impl->mStrong)

當咱們在這裏對目標對象的強引用計數執行加1操做時,要保證原子性,由於其它地方也有可能正在對這個目標對象的強引用計數執行加1的操做,前面咱們通常是調用android_atomic_inc函數來完成,可是這裏是經過調用android_atomic_cmpxchg函數來完成,android_atomic_cmpxchg函數是體系結構相關的函數,在提供了一些特殊的指令的體系結構上,調用android_atomic_cmpxchg函數來執行加1操做的效率會比調用android_atomic_inc函數更高一些。函數android_atomic_cmpxchg是在system/core/include/cutils/Atomic.h文件中定義的一個宏:

int android_atomic_release_cas(int32_t oldvalue, int32_t newvalue,
                               volatile int32_t* addr)
{
    ......
}

它實際執行的函數是android_atomic_release_cas,這個函數的工做原理大概是這樣的:若是它發現*addr == oldvalue,就會執行*addr = newvalue的操做,而後返回0,不然什麼也不作,返回1。在咱們討論的這個場景中,oldvalue等於curCount,而newvalue等於curCount + 1,因而,在*addr == oldvalue的條件下,就至關因而對目標對象的強引用計數值增長了1。什麼狀況下*addr != oldvalue呢?在調用android_atomic_release_cas函數以前,oldvalue和值就是從地址addr讀出來的,若是在執行android_atomic_release_cas函數的時候,有其它地方也對地址addr進行操做,那麼就會有可能出現*addr != oldvalue的狀況,這時候就說明其它地方也在操做目標對象的強引用計數了,所以,這裏就不能執行增長目標對象的強引用計數的操做了,它必需要等到其它地方操做完目標對象的強引用計數以後再從新執行,這就是爲何要經過一個while循環來執行了。

在代碼6的最後部分經過判斷curCount是否大於等於INITIAL_STRONG_VALUE,對於第一種狀況作出處理,對於已經curCount等於INITIAL_STRONG_VALUE+1的情景進行處理,調用android_atomic_cmpxchg函數將INITIAL_STRONG_VALUE值從curCount中減掉。

if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE,
        &impl->mStrong) == 0) {
    break;
}

第二種狀況比較複雜一點,由於這時候目標對象可能還存在,也可能不存了,這要根據實際狀況來判斷。若是對象沒有設置了弱引用保護,也就意味着當強引用計數爲0的時候對象將會被銷燬。目前處於了目標對象還歷來沒有被強引用計數過,curCount == INITIAL_STRONG_VALUE,此時弱指針可以被提高爲強指針作出的操做是:

while (curCount > 0) {
    if (android_atomic_cmpxchg(curCount, curCount + 1,
            &impl->mStrong) == 0) {
        break;
    }
    // the strong count has changed on us, we need to re-assert our
    // situation (e.g.: another thread has inc/decStrong'ed us)
    curCount = impl->mStrong;
}

爲curCount作加1操做。若是設置了弱引用保護,也就意味着強引用計數爲0,弱引用計數不爲0的時候對象尚未被銷燬。使用impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)強引用指針提高爲強引用指針,同時強引用計數自加1。

if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {
    // it didn't so give-up.
    decWeak(id);
    return false;
}
// grab a strong-reference, which is always safe due to the
// extended life-time.
curCount = android_atomic_inc(&impl->mStrong);

弱指針提高爲強指針,就須要進一步調用目標對象的onIncStrongAttempted來看看是否容許這種狀況發生,這又該怎麼理解呢?能夠這樣理解,目標對象的設計者可能自己就不但願這個對象被強指針引用,只能經過弱指針來引用它,所以,這裏它就能夠重載其父類的onIncStrongAttempted函數,而後返回false,這樣就能夠阻止弱指針都被提高爲強指針。在RefBase類中,其成員函數onIncStrongAttempted默認是返回true的:

bool RefBase::onIncStrongAttempted(uint32_t flags, const void* /*id*/)
{
    return (flags&FIRST_INC_STRONG) ? true : false;
}

分析到這裏,弱指針就介紹完了。強指針和弱指針的關係比較密切,同時它們也比較複雜,下面咱們再舉一個例子來講明強指針和弱指針的用法,同時也驗證一下它們的實現原理。

##5.智能指針實例

這裏經過一個C++例子來看,筆者這裏使用了Eclipse IDE for C/C++ Developers,將android_atomic_inc(&mCount)依賴於系統給操做變成了mCount++android_atomic_dec(&mCount)也是依賴於操做系統,這裏變成mCount--。其中ALOG_ASSERT也是採用了簡單的替代方案,固然也能夠詳細打出具體的ALOG_ASSERT信息,以下所示:

#define ALOG_ASSERT(cond, log, ...) LOG_ALWAYS_FATAL_IF(cond, log, ...)
#define LOG_ALWAYS_FATAL_IF(cond, log, ...) \
    ( (cond) \
    ? (void)(printf("", log)) \
    : (void)0 )

咱們先來看第一個例子,旨在測試的目標爲:

  1. 在實際工程中使用sp強指針和wp弱指針,查看強弱引用計數的變化。
  2. 測試在無弱引用保護下,wp弱指針升強指針的使用。

[代碼7 - 測試強指針和弱指針]

#define INITIAL_STRONG_VALUE (1<<28)

class WeightClass : public RefBase{
public:
	void printRefCount() {
		int32_t strong = getStrongCount();
		weakref_type* ref = getWeakRefs();
		cout << "-----------------------" << endl;
		cout << "Strong Ref Count: " << (strong == INITIAL_STRONG_VALUE ? 0 : strong) << endl;
		cout << "Weak Ref Count: " << ref->getWeakCount() << endl;
		cout << "-----------------------" << endl;
	}
};

class StrongClass: public WeightClass {
public:
	StrongClass() {
	}
	virtual ~StrongClass() {
	}
};

void TestStrongClass(StrongClass* pStrongClass) {
	pStrongClass->printRefCount();
	wp<StrongClass> wpOut = pStrongClass;
	pStrongClass->printRefCount();
	{
		sp<StrongClass> spInner = pStrongClass;
		pStrongClass->printRefCount();
	}
	sp<StrongClass> spOut = wpOut.promote();
	cout << "spOut: " << spOut.get()<<endl;
	pStrongClass->printRefCount();
}

int main(int argc, char** argv) {
	cout << "Test Strong Class" << endl;
	StrongClass* pStrongClass = new StrongClass();
	TestStrongClass(pStrongClass);
	return 0;
}

代碼輸出結果爲:

Test Strong Class
-----------------------
Strong Ref Count: 0
Weak Ref Count: 0
-----------------------
-----------------------
Strong Ref Count: 0
Weak Ref Count: 1
-----------------------
-----------------------
Strong Ref Count: 1
Weak Ref Count: 2
-----------------------
spOut: 0
-----------------------
Strong Ref Count: 5313792
Weak Ref Count: 5322744
-----------------------

從結果中能夠看出,沒有使用智能指針的時候強弱計數都爲0,使用wp<StrongClass> wpOut聲明指向類實例pStrongClass的時候,弱引用計數從0增長至1,強引用計數仍是0不增長。使用sp<StrongClass> spInner聲明指向類實例pStrongClass的時候,弱引用計數從1增長至2,強引用計數從0增長至1。跳出強指針做用域的時候,sp<StrongClass> spInner指針調用sp<T>析構函數,智能指針sp執行decStrong函數,同時發現強引用計數爲1,釋放類實例pStrongClass。而後釋放weakref_impl對象。最後結果spOut: 0。

接下來咱們在看一個例子,旨在測試的目標爲:

  1. 在實際工程中使用sp強指針和wp弱指針,查看強弱引用計數的變化。
  2. 測試在有弱引用保護下,wp弱指針升強指針的使用。

[代碼8 - 弱引用保護下的弱升強]

#define INITIAL_STRONG_VALUE (1<<28)

class WeightClass : public RefBase{
public:
	void printRefCount() {
		int32_t strong = getStrongCount();
		weakref_type* ref = getWeakRefs();
		cout << "-----------------------" << endl;
		cout << "Strong Ref Count: " << (strong == INITIAL_STRONG_VALUE ? 0 : strong) << endl;
		cout << "Weak Ref Count: " << ref->getWeakCount() << endl;
		cout << "-----------------------" << endl;
	}
};
class WeakClass: public WeightClass {
public:
	WeakClass() {
		extendObjectLifetime(OBJECT_LIFETIME_WEAK);
	}

	virtual ~WeakClass() {
	}
};

void TestWeakClass(WeakClass* pWeakClass) {
	pWeakClass->printRefCount();
	wp<WeakClass> wpOut = pWeakClass;
	pWeakClass->printRefCount();
	{
		sp<WeakClass> spInner = pWeakClass;
		pWeakClass->printRefCount();
	}
	pWeakClass->printRefCount();
	sp<WeakClass> spOut = wpOut.promote();
	cout << "spOut: " << spOut.get()<<endl;
	pWeakClass->printRefCount();
}

int main(int argc, char** argv) {
	cout << "Test Weak Class: " << endl;
	WeakClass* pWeakClass = new WeakClass();
	TestWeakClass(pWeakClass);
	return 0;
}

代碼輸出結果爲:

Test Weak Class: 
-----------------------
Strong Ref Count: 0
Weak Ref Count: 0
-----------------------
-----------------------
Strong Ref Count: 0
Weak Ref Count: 1
-----------------------
-----------------------
Strong Ref Count: 1
Weak Ref Count: 2
-----------------------
-----------------------
Strong Ref Count: 0
Weak Ref Count: 1
-----------------------
spOut: 0x551500
-----------------------
Strong Ref Count: 1
Weak Ref Count: 2
-----------------------

從結果中能夠看出,沒有使用智能指針的時候強弱計數都爲0,使用wp<WeakClass> wpOut聲明指向類實例pWeakClass的時候,弱引用計數從0增長至1,強引用計數仍是0不增長。使用sp<WeakClass> spInner聲明指向類實例pStrongClass的時候,弱引用計數從1增長至2,強引用計數從0增長至1。跳出強指針做用域的時候,sp<StrongClass> spInner指針調用sp<T>析構函數,智能指針sp執行decStrong函數,同時發現強引用計數爲1,可是發現存在弱引用保護,其中mFlag==OBJECT_LIFETIME_WEAK,不釋放類實例pWeakClass。出來函數中的做用域發現強引用計數爲0,弱引用計數爲1。而後調用wpOut.promote()方法,作一個弱升強的操做,此時spOut對象已經存在。查看相應的引用計數:強引用計數爲1,弱引用計數爲2。

##6.總結

智能指針sp和wp以及計數基類LightRefBase、RefBase的配合實現了對象的及時釋放,大大下降了內存泄漏的可能性,在Android底層JNI中獲得了普遍的使用,爲代碼的安全可用保駕護航。

相關文章
相關標籤/搜索