android sp wp 解析

原文地址:http://blog.csdn.net/xuqiqiang1993/article/details/68923160



Android系統中,Native層的代碼基本都是C++寫的,C++跟Java不一樣,C++沒有垃圾回收機制,C++代碼中難於管理new出來對象的釋放,稍有不慎就造成內存泄漏。針對此問題,Android中提出了一套類似Java垃圾回收機制的智能指針,採用強指針sp(Strong Pointer)和弱指針wp(Weak Pointer)對目標對象進行應用,實現對象的自動回收。下面我們將從C++的基礎知識入手,對Android的智能指針展開逐步的分析。

1      相關基礎知識

Android的智能指針,巧妙的運用C++的基礎特性,實現對象的自動釋放,我們就先來看看都用了C++的什麼特性。

1.1        作用域

標記變量的有效範圍。從作用域上來看,可以將對象分爲全局對象、局部對象、靜態全局對象和靜態局部對象。

一般來說,局部變量的有效作用域從它的定義點開始,到和定義變量之前最鄰近的開括號配對的第一個閉括號,也就是說,作用域由變量所在的最近一對{}括號確定。

  [演示代碼1]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. void testScope() {  
  2.        SheepbigSheep; //局部對象  
  3.        {  
  4.               SheepsmallSheep; // 局部對象  
  5.        } // smallSheep的作用域結束  
  6. // bigSheep的作用域結束  

1.2        對象內存空間的分配、釋放

從內存分配空間來看,可將對象分爲棧對象和堆對象。棧對象在作用域結束後會自動釋放,而堆對象需要手動顯示的釋放。

  [演示代碼2]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. void testMemoryMap() {  
  2.        Sheepsheep; // 棧對象,testMemoryMap調用結束後自動釋放  
  3.        Sheep*pSheep; // 堆對象,需要手動釋放  
  4.        deletepSheep; // 釋放pSheep指向的對象  
  5.        pSheep= 0; //將pSheep指向NULL,防止造成野指針  
  6. }  

圖1-1是內存空間的分配示意圖。


圖 1‑1 內存空間的分配、釋放

1.3        原子操作函數

定義在system/core/libcutils/Atomic.c,依賴於具體的芯片平臺。原子操作函數特點:線程安全,返回舊值。

int32_t android_atomic_add(int32_t increment, volatile int32_t *ptr)

加函數,返回舊值,*ptr = *ptr + increment

int32_t android_atomic_inc(volatile int32_t *addr)

自增操作,返回舊值,*ptr = *ptr + 1

int32_t android_atomic_dec(volatile int32_t *addr)

自減操作, 返回舊值,*ptr = *ptr - 1

int32_t android_atomic_and(int32_t value, volatile int32_t *ptr)

位與操作,返回舊值,*ptr = *ptr & value

int32_t android_atomic_or(int32_t value, volatile int32_t *ptr)

位或操作,返回舊值,*ptr = *ptr | value

int android_atomic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t *ptr)

如果*addr == oldvalue,就會執行*addr = new_value的操作,然後返回0,否則返回1

表 1‑1 Android原子操作函數一覽表

1.4        引用計數的原理

棧對象在生命週期,即作用域結束後自動釋放,所以我們這裏討論的是堆對象的引用,也就是指針對象。

圖1-2是指針引用時,利用引用數管理實際對象釋放的原理圖。

引用計數的原理很簡單,當引用某個對象時,使其引用數+1;引用結束時,使其引用數-1;當引用數爲0時,delete掉實際對象。

根據前面的原理,引出兩個問題,帶着這兩個問題,我們來看看Android是怎麼實現的。

Ø  怎麼管理引用數?

Ø  怎麼判斷引用開始和結束,怎麼增減引用數?

2      Android智能指針原理

Android設計了基類RefBase,用以管理引用數,所有類必須從RefBase派生,RefBase是所有對象的始祖。

設計模板類sp、wp,用以引用實際對象,sp強引用和wp弱引用。sp、wp聲明爲棧對象,作用域結束時,自動釋放,自動調用析構函數。因此,可以在sp、wp的構造函數中,增引用數;在析構函數中,減少引用計數。

專門設計weakref_impl類,該類是RefBase的內部類,用來做真正引用數管理,創建實際對象時,同時創建一個mRefs對象。不管是強引用和弱應用,都由mRefs來管理。

圖2-1 展示Android智能指針的關係類圖。


圖 2‑1 Android智能指針關係圖

看了智能指針的實現原理,我們來看看具體的實現是什麼樣的。

3      智能指針的實現

根據前面的原理,Android設計了強引用sp和弱引用wp,故實際對象的釋放,可分爲強引用控制和弱引用控制。所謂強引用控制,指的是強引用數mStrong爲0時,釋放實際對象;弱引用控制,則指的是弱引用數mWeak爲0時,才釋放實際對象。

下面將結合一些實例,分析具體的實現。我們先來看一段代碼實例。

  [代碼實例1]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. class Sheep: public RefBase { // 羊年,定義Sheep從RefBase派生  
  2. public:  
  3.        Sheep(): RefBase() { }// 可顯示調用RefBase的構造,也可以不用  
  4.        virtual~Sheep() { }// 最好聲明爲virtual,以便從Sheep派生  
  5. };  
  6. void testSheep() {  
  7.        Sheep*pSheep = new Sheep(); // new一個Sheep對象,這個是一個堆對象  
  8.        { // 限定sp的作用域  
  9.               sp<Sheep>spSheep(pSheep); // spSheep是一個棧對象  
  10.               {// 限定wp的作用域  
  11.                      wp<Sheep>wpSheep(pSheep);  
  12.               }//調用wp的析構函數  
  13.        } // 調用sp的析構函數,實際對象pSheep已釋放,若再使用pSheep將會出錯  
  14. }  


3.1        RefBase構造和mRefs

在實例代碼中,我們先定義了一個類Sheep,從RefBase派生,創建了一個實際對象,pSheep 指向實際對象。

在構造Sheep的實際對象時,將調RefBase的構造函數。RefBase的構造函數如下,在構造函數中創建mRefs。

weakref_impl從weakref_type派生,mRefs纔是真正的“管家”。

  [system/core/libutils/RefBase.cpp]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. RefBase::RefBase()  
  2.     :mRefs(new weakref_impl(this)) // 真正管理引用計數  
  3. {}  
  4. weakref_impl(RefBase* base)  
  5.     :mStrong(INITIAL_STRONG_VALUE) // 1<<28(268435456),爲什麼不是0?  
  6.     ,mWeak(0)  
  7.     ,mBase(base) // mBase指向實際對象  
  8.     ,mFlags(0) // 這個標識很重要,指定是強應用控制還是弱引用控制  
  9. {}  

請注意這裏的mFlags,默認值爲0,可通過修改這個標誌來設置是強引用控制,還是弱引用控制,代碼如下:

  [system/core/include/utils/RefBase.h]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. enum {  
  2.    OBJECT_LIFETIME_STRONG  = 0x0000,  
  3.    OBJECT_LIFETIME_WEAK    = 0x0001,  
  4.    OBJECT_LIFETIME_MASK    = 0x0001  
  5. };  

mFlags默認爲0,即OBJECT_LIFETIME_STRONG,強引用控制。設置爲OBJECT_LIFETIME_WEAK時,爲弱引用控制。可以通過extendObjectLifetime函數修改,代碼如下:

 [system/core/libutils/RefBase.cpp]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. void RefBase::extendObjectLifetime(int32_t mode)  
  2. {  
  3.    android_atomic_or(mode, &mRefs->mFlags);  
  4. }  

3.2        sp構造

接下來,我們創建了一個sp對象spSheep,這是一個棧對象,在其作用域結束後將自動釋放,調用sp的析構函數。

  [system/core/include/utils/StrongPointer.h]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. template<typename T>  
  2. sp<T>::sp(T* other)  
  3.         :m_ptr(other) {  
  4.     if(other)  
  5.        other->incStrong(this);  
  6. }  

other指向真正的Sheep對象,在sp的構造函數中,將other賦值給了sp的m_ptr,m-ptr就指向了真正的Sheep對象。

因而,other->incStrong(this),實際就是Sheep的父類RefBase的incStrong函數,代碼如下:

  [system/core/libutils/RefBase.cpp]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. void RefBase::incStrong(const void* id) const  
  2. {  
  3.    weakref_impl* const refs = mRefs;  
  4.    refs->incWeak(id); // 調用incWeak函數  
  5.      
  6.    refs->addStrongRef(id); // 由DEBUG_REFS控制,release版本什麼也不做  
  7.     constint32_t c = android_atomic_inc(&refs->mStrong); // 強引用數+1,c爲舊值  
  8.    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strongref", refs);  
  9.     if (c !=INITIAL_STRONG_VALUE)  { //判斷是否是第一次引用  
  10.        return;  
  11.     } // 第一次引用,refs->mStrong爲1<<28 +1 (268435457)  
  12.    
  13.    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);  
  14.     // refs->mStrong爲1  
  15.    refs->mBase->onFirstRef(); //第一次引用時調用  
  16. }  

在incStrong函數中調用refs 的incWeak函數,incWeak的代碼如下:

  [system/core/libutils/RefBase.cpp]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. void RefBase::weakref_type::incWeak(const void*id)  
  2. {  
  3.    weakref_impl* const impl = static_cast<weakref_impl*>(this);  
  4.    impl->addWeakRef(id); // 由DEBUG_REFS控制,release版本什麼也不做  
  5.     constint32_t c __unused = android_atomic_inc(&impl->mWeak); //弱引用數+1  
  6.    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weakref"this);  
  7. }  

OK,sp構造完成,增加一次強引用。sp構造完成後,mRefs的強引用數變爲1,弱引用數也變爲1;第一次強引用時,回調onFirstRef()。

3.3        wp構造

接下來,我們創建了一個wp對象wpSheep,這是一個棧對象,在其作用域結束後將自動釋放,調用wp的析構函數。

  [system/core/include/utils/RefBase.h]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. template<typename T>  
  2. wp<T>::wp(T* other)  
  3.     :m_ptr(other)  
  4. {  
  5.     if(other) m_refs = other->createWeak(this);  
  6. }  

other指向真正的Sheep對象,在wp的構造函數中,將other賦值給了wp的m_ptr,m-ptr就指向了真正的Sheep對象。

因而,other-> createWeak (this),實際就是Sheep的父類RefBase的createWeak函數,代碼如下:

  [system/core/libutils/RefBase.cpp]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. RefBase::weakref_type* RefBase::createWeak(constvoid* id) const  
  2. {  
  3.    mRefs->incWeak(id); // incWeak函數前面分析過,最終的結果就是弱引用數+1  
  4.     returnmRefs;  
  5. }  

reateWeak時,調用incWeak,最終的影響是弱引用數+1。現在,我們的實例中,強引用數爲1,弱引用數爲2。

返回值爲mRefs,也就是m_refs和mRefs指向同一個weakref_impl對象,而mRefs的mBase指向真正的對象Sheep。因此此處的spSheep和wpSheep都是管理同一個真正的對象。

3.4        wp析構

繼續看我們的實例代碼,現在wpSheep的作用域結束,將調wp的析構函數,wp析構函數的代碼如下:

  [system/core/include/utils/RefBase.h]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. template<typename T>  
  2. wp<T>::~wp()  
  3. {  
  4.     if(m_ptr) m_refs->decWeak(this); // 調用decWeak函數  
  5. }  

在wp的析構函數中調用m_refs的decWeak函數。m_refs和mRef指向同一個weakref_impl對象,decWeak代碼如下:

  [system/core/libutils/RefBase.cpp]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. void RefBase::weakref_type::decWeak(const void*id)  
  2. {  
  3.    weakref_impl* const impl = static_cast<weakref_impl*>(this);  
  4.    impl->removeWeakRef(id);  
  5.     constint32_t c = android_atomic_dec(&impl->mWeak); // 弱引用數-1,c爲舊值  
  6.    ALOG_ASSERT(c >= 1, "decWeak called on %p too many times",this);  
  7.     if (c !=1) return//c爲舊值,判斷是否是最後一次弱引用  
  8.     // 記得前面我們說的,mFlags爲0,我們並沒有改變它  
  9.     if((impl->mFlags&OBJECT_LIFETIME_WEAK) ==  
  10.             OBJECT_LIFETIME_STRONG){  
  11.         // 強引用控制,是否釋放實際對象是根據強引用數  
  12.         if(impl->mStrong == INITIAL_STRONG_VALUE) {  
  13.            delete impl->mBase; // 根本就沒有強引用引用實際對象,釋放實際對象  
  14.         }else {  
  15.            delete impl; // 釋放mRefs  
  16.         }  
  17.     } else {  
  18.        impl->mBase->onLastWeakRef(id); //最後一次弱引用時調用  
  19.         if((impl->mFlags&OBJECT_LIFETIME_MASK) ==  
  20.                         OBJECT_LIFETIME_WEAK) {  
  21.            delete impl->mBase; //弱引用控制,釋放實際對象  
  22.         }  
  23.     }  
  24. }  

wp析構,情況比較複雜,總的說來做了以下幾件事:

Ø  弱引用數減1。

Ø  最後一次弱引用時,強引用控制,釋放mRefs,若沒有強引用,釋放實際對象

Ø  最後一次弱引用時,弱引用控制,釋放實際對象

就我們的實例來看,此時,強引用數爲1,弱引用數爲1,並沒有任何釋放。

3.5        sp析構

在我們的實例代碼中,wp析構完後,sp的作用域也就結束了。此時,會調用sp的析構函數,代碼如下:

  [system/core/include/utils/StrongPointer.h]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. template<typename T>  
  2. sp<T>::~sp() {  
  3.     if(m_ptr)  
  4.        m_ptr->decStrong(this);  
  5. }  

在析構函數中調用m_ptr的decStrong函數,m_ptr指向實際對象。此處爲Sheep的父類RefBase的decStrong函數,代碼如下:

  [system/core/libutils/RefBase.cpp]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. void RefBase::decStrong(const void* id) const  
  2. {  
  3.    weakref_impl* const refs = mRefs;  
  4.     refs->removeStrongRef(id);// 由DEBUG_REFS控制,release版本什麼也不做  
  5.     constint32_t c = android_atomic_dec(&refs->mStrong); // 強引用數-1  
  6.    ALOG_ASSERT(c >= 1, "decStrong() called on %p too manytimes", refs);  
  7.     if (c ==1) { // c爲舊值,c爲1時,即強引用數爲0  
  8.        refs->mBase->onLastStrongRef(id); //最後一次強引用結束時調用  
  9.         if((refs->mFlags&OBJECT_LIFETIME_MASK) ==  
  10.                         OBJECT_LIFETIME_STRONG){  
  11.            delete this// 若是強引用控制,釋放實際對象,調實際對象的析構函數  
  12.         }  
  13.     }  
  14.    refs->decWeak(id);  

refs的decWeak函數,前面wp析構的時候分析過,這裏不再重複。sp析構完成,主要完成以下工作:

Ø  強引用數減1,弱引用數據減1。

Ø  最後一次強引用時,若是強引用控制,釋放實際對象,釋放mRefs,調用onLastStrongRef函數。

在我們的代碼中,此時強引用數爲0,弱引用數爲0,實際對象的析構函數將被調用,mRefs將被釋放。下面我看看實際對象的析構函數。

3.6        RefBase析構

實際對象的析構,先析構RefBase,RefBase的析構函數如下:

  [system/core/libutils/RefBase.cpp]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. RefBase::~RefBase()  
  2. {  
  3.     if(mRefs->mStrong == INITIAL_STRONG_VALUE) {  
  4.        delete mRefs; // 沒有強引用引用實際對象,釋放mRefs  
  5.     } else {  
  6.         if((mRefs->mFlags & OBJECT_LIFETIME_MASK) !=  
  7.                         OBJECT_LIFETIME_STRONG){  
  8.            if (mRefs->mWeak == 0) {  
  9.                delete mRefs; // 釋放mRefs指向的對象  
  10.            }  
  11.         }  
  12.     }  
  13.     // fordebugging purposes, clear this.  
  14.    const_cast<weakref_impl*&>(mRefs) = NULL; // mRefs指向0,避免野指針  
  15. }  

OK,RefBase析構分析完了,在RefBase的析構函數中主要的工作就是釋放mRefs指向的weakref_impl的對象。

到此,我們的實例代碼分析完成,我們首先構造一個Sheep對象,pSheep指向實際對象。再分別構造一個強引用sp和一個弱引用wp,用以引用實際對象,實際對象的釋放就由sp和wp控制,我們並沒有顯示的釋放構造的pSheep指向的實際對象。

我們來看看實例代碼1中,對象的構造和析構Log:

  [示例代碼1的Log]

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. D/       (13624): Sheep::------------------testSheepstart--------------------------  
  2. D/       (13624): Sheep::Sheep constructor invoked this=0xb6301080  
  3. D/       (13624): Sheep:: No refs, strong count=268435456, weak count=0  
  4. D/       (13624): Sheep::in sp scope ------------  
  5. D/       (13624): Sheep::onFirstRef, object=0xb6301080  
  6. D/        (13624): Sheep:: After strong ref, strongcount=1, weak count=1  
  7. D/       (13624): Sheep::in wp scope ------------  
  8. D/       (13624): Sheep:: After weak ref, strong count=1, weak count=2  
  9. D/       (13624): Sheep::out wp scope ------------  
  10. D/        (13624):Sheep:: release weak ref, strong count=1, weak count=1  
  11. D/       (13624): Sheep::out sp scope ------------  
  12. D/       (13624): Sheep::onLastStrongRef, id=0xbec42884  
  13. D/       (13624): Sheep::Sheep destructor invoked this=0xb6301080  
  14. D/       (13624): Sheep::--------------------testSheepend--------------------------  

3.7        實際對象的狀態

通過前面的分析,我們可以繪製出實際對象的狀態圖,如下如所示:


圖 3‑1 實際對象的狀態圖

4      智能指針的使用

前面我們通過示例代碼1,知道了智能指針是怎麼管理實際對象的,怎麼控制實際對象的釋放的。但是我們只是分析了其中的構造函數和析構函數,下面我們將對智能指針做全面的瞭解。

4.1        RefBase的特性

我們先看看RefBase的類圖,如圖4-1所示。


圖 4‑1 RefBase類圖

Ø  所有類須從RefBase派生,只有一個無參構造函數,RefBase析構函數需申明爲virtual。

Ø  在構造函數中創建mRefs對象,爲weakref_impl類型。

Ø  可以在派生類中通過函數extendObjectLifetime指定是強引用控制,還是弱引用控制,默認爲強引用控制。

Ø  在析構函數中,判斷是否釋放mRefs。

Ø  私有的構造函數和賦值運算重載,不允許子類使用。

Ø  獲取實際對象的強引用數getStrongCount

Ø  子類可派生virtual成員函數,獲知自身的引用情況。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章