Android – 引用計數(sp、wp、Refbase)

Boost和Loki是應用比較廣泛的引用計數方案,Android提供瞭另外一個引用計數方案,就是sp、wp和Refbase組合。

強引用和弱引用區別

在Android裡面,sp是強引用,它是應用最多的引用形式,而且後面的分析,我們將知道,強引用直接管理著對象的銷毀;wp是弱引用,弱引用的用途是能夠對某個對象進行引用,但是即使該對象弱引用還存在,這個對象也可能會被銷毀。弱引用看上去更復雜一些,以下是在網上摘錄的一句話,“弱引用適合那些數據成員特別多,而且重新創建又相對容易的類,也就是俗稱的胖子類,建立弱引用可以引用對象,但也不阻止其被垃圾回收,在內存的使用方面取得一定的平衡”。還是看看實際的例子比較好,如Android Framework/base/services/camera/libcamera/cameraservice.cpp中的一段代碼:

[html] 

sp<Client> client;  

    if (cameraId < 0 || cameraId >= mNumberOfCameras) {  

        LOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).",  

            callingPid, cameraId);  

        return NULL;  

    }  

  

    Mutex::Autolock lock(mServiceLock);  

    if (mClient[cameraId] != 0) {  

        client = mClient[cameraId].promote();  

        if (client != 0) {  

            if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) {  

                LOG1("CameraService::connect X (pid %d) (the same client)",  

                    callingPid);  

                return client;  

            } else {  

                LOGW("CameraService::connect X (pid %d) rejected (existing client).",  

                    callingPid);  

                return NULL;  

            }  

        }  

        mClient[cameraId].clear();  

    }  

代碼說明

該代碼是函數connect的一部分,connect函數是客戶端camera.cpp連接cameraservice以獲得獲得服務。Android的cameraservice機制是可以支持多個攝像頭同時供多個客戶端使用,並且,每個攝像頭可能會被多個客戶端輪流使用(你可以想象,在不關閉攝像頭的情況下,兩個應用程序交替顯示一個攝像頭的圖像,如果幀數足夠多,那麼這個攝像頭采集的數據就可以同時供兩個應用程序順暢顯示)。那麼應用程序A和B是如何交替使用呢,就是分別調用service提供的connect函數,而mClient數組就是service部分將具體的某個攝像頭和某個具體的應用程序掛鉤的變量(該變量就是CameraService::Client類對象),從這也可以看出,一個時間點,一個攝像頭隻能同時供一個應用程序使用。所以,在交替切換過程中,就伴隨著CameraService::Client類對象的創建和銷毀。

這個時候之所以使用弱引用更方便,是因為如果程序A正在使用攝像頭,如果按照強引用,程序B是無法從程序A搶到攝像頭的;而如果是弱引用,即使程序A占用攝像頭,程序B依然可以將程序A在CameraService的資源釋放,然後創建自己的資源,然後將建立自己的弱引用。這確實很巧妙。

所以弱引用適合用在資源可以被多個程序同時交替使用,時刻可能被其他程序搶走資源的情況,而且資源可以自己負責,也可以自己不負責的情況。

 

sp如何管理強引用計數

sp指的是對象的強引用,一般定義如下,sp<Camera> mCamera,那麼,mCamera對應的實際對象就交由sp進行管理瞭,當引用計數為0時,該實際對象就會被銷毀。那麼在什麼情況要增加引用計數,在什麼情況要減少引用計數呢?

以下是增加引用計數的情況:

1. 指針賦值拷貝

如:

sp<Camera> mCamera = new camera();

這是一個用得非常多的情況,當new一個對象時,就將該對象的指針交由sp進行管理瞭,那麼這個時候sp就應該增加這個new對象的引用計數,當然這個時候應該是1;

Camera* c = new camera();

sp<Camera> mCamera = c;

該情況是將一個已經new好瞭的對象指針賦值給mCamera,那麼這個時候sp就應該增加這個new對象的引用計數。

sp相應代碼如下:

[html]  

template<typename T>  

sp<T>& sp<T>::operator = (T* other)  

{  

    if (other) other->incStrong(this);  

    if (m_ptr) m_ptr->decStrong(this);  

    m_ptr = other;  

    return *this;  

}  

其中m_ptr是對象的指針,該代碼的邏輯如下

# 如果被復制的對象存在,即other不為NULL,那麼就將該對象的引用計數加1;

# 如果原來m_ptr已經指向瞭某個對象,那麼就將某個對象的引用計數減1;

# 然後將other賦值給m_ptr,從此,m_ptr就指向瞭剛剛設置的對象;

 

2. 引用賦值拷貝

如:

sp<Camera> c = new camera();

sp<Camera> mCamera = c;

將一個對象c賦值給mCamera,sp對應代碼如下:

[html]  

template<typename T>  

sp<T>& sp<T>::operator = (const sp<T>& other) {  

    T* otherPtr(other.m_ptr);  

    if (otherPtr) otherPtr->incStrong(this);  

    if (m_ptr) m_ptr->decStrong(this);  

    m_ptr = otherPtr;  

    return *this;  

}  

# 將被賦值對象other的對象引用計數加1;

# 將sp對應對象的引用計數減1;

# 將sp的對象改為other對象;

 

3. 指針構造拷貝

如:

sp<Camera> mCamera(new camera());

sp相應代碼如下:

[html]  

template<typename T>  

sp<T>::sp(T* other)  

    : m_ptr(other)  

{  

    if (other) other->incStrong(this);  

}  

# 將被賦值對象other的對象指針m_ptr賦值給當前sp的m_ptr;

# 如果被賦值對象other的對象存在,則將該對象的引用計數加1;

 

4. 引用構造拷貝 

如:

sp<Camera> c = new camera();

sp<Camera> mCamera(c);

sp相應代碼如下:

[html] 

template<typename T>  

sp<T>::sp(const sp<T>& other)  

    : m_ptr(other.m_ptr)  

{  

    if (m_ptr) m_ptr->incStrong(this);  

}  

# 將被賦值對象other的對象指針m_ptr賦值給當前sp的m_ptr;

# 如果被賦值對象other的對象存在,則將該對象的引用計數加1;

 

以下是減少引用計數的情況:

1. sp析構

如:

{

sp<Camera> c = new camera();

/* do something*/

}

由於c是局部變量,那麼,在大括號結束前,c將被析構。

sp對應代碼如下:

[html]  

template<typename T>  

sp<T>::~sp()  

{  

    if (m_ptr) m_ptr->decStrong(this);  

}  

# 析構並不代表對象真的銷毀,因為可能將該對象賦值給瞭其他sp管理,所以,析構函數隻是將對象的引用計數減1;

 

2. 指針賦值拷貝和引用賦值拷貝

在上面的增加引用計數已經介紹,重新設置sp管理的對象,必須將現在管理的對象的引用計數減1

 

3. 清除

如:

sp<Camera> mCamera = new camera();

mCamera.clear();

意思設置mCamera不再管理任何對象。

sp代碼如下:

[html]  

template<typename T>  

void sp<T>::clear()  

{  

    if (m_ptr) {  

        m_ptr->decStrong(this);  

        m_ptr = 0;  

    }  

}  

# 將管理的對象引用計數減1;

# 將m_ptr置空;

 

wp如何管理弱引用計數

對於wp的處理和sp基本相同,代碼部分就不做細致分析瞭。

我們可以記住,弱引用計數可以通過promote函數升級為強引用。

 

Refbase機制

我們在上面一節可以看到,sp和wp就是調用Refbase提供的incStrong、decStrong、incWeak和decWeak設置對象的引用計數,那麼Refbase是如何實現的呢,以及什麼情況下才會將對象銷毀呢?

如:

{

sp<Camera> mCamera = new camera();

}

上面如此簡單的代碼,sp先將camera對象的引用計數加1,然後由於作用域結束,所以mCamera被析構,析構時會將camera對象的引用計數減1,那麼這個時候,這個對象的引用計數恰好是0,那麼這個時候對象會被銷毀嗎?我們還是看看Refbase是如何被構造,如何管理引用計數,如何管理對象的。

構造

[html]  

RefBase::RefBase()  

    : mRefs(new weakref_impl(this))  

{  

//    LOGV("Creating refs %p with RefBase %p\n", mRefs, this);  

}  

很簡單,隻是又創建瞭一個weakref_impl,從這個名字就可以看出它是處理弱引用的類,其實除瞭弱引用,它還處理強引用。我們還是根據《心得 – 如何讀大型代碼和寫代碼》所描述的方法,我們隻需要從宏觀上瞭解weakref_impl的作用,我們現在正在研究Refbase,所以,隻要在調用weakref_impl的地方,我們隻要能夠猜出它完成瞭什麼功能即可,不進行深入分析。

# 創建瞭weakref_impl對象,並將this指針傳遞過去,看來weakref_impl對象要通過this指針來回調Refbase的某些方法;

 

增加強引用計數

mCamera指針賦值拷貝時會增加引用計數,代碼如下:

[html]  

void RefBase::incStrong(const void* id) const  

{  

    weakref_impl* const refs = mRefs;  

    refs->addWeakRef(id);  

    refs->incWeak(id);  

      

    refs->addStrongRef(id);  

    const int32_t c = android_atomic_inc(&refs->mStrong);  

    LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);  

#if PRINT_REFS  

    LOGD("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);  

    const_cast<RefBase*>(this)->onFirstRef();  

}  

# 增加弱引用,可見,使用強引用時,還伴隨著一個弱引用;

# 增加強引用;

# 如果是第一次增加強引用計數,那麼就會調用onFirstRef函數,我們在代碼經常會看到onFirstRef函數,通過該函數,經常會做些初始化方面的工作;

 

減少強引用計數

當mCamera析構時就會減少強引用計數,代碼如下:

[html]  

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  

    LOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);  

#endif  

    LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);  

    if (c == 1) {  

        const_cast<RefBase*>(this)->onLastStrongRef(id);  

        if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {  

            delete this;  

        }  

    }  

    refs->removeWeakRef(id);  

    refs->decWeak(id);  

}  

 

# 減小強引用計數;

# 銷毀對象,如果減小強引用計數之前,強引用計數是1,也就是減小後,強引用計數是0,那麼這個時候就delete this瞭;

# 減小弱引用計數,看來弱引用計數和對象實際存不存在沒有必然的關系;

 

weakref_type功能

我們通過Refbase看出,weakref_type實際管理著強引用和弱引用兩個計數。

 

構造

[html] 

weakref_impl(RefBase* base)  

    : mStrong(INITIAL_STRONG_VALUE)  

    , mWeak(0)  

    , mBase(base)  

    , mFlags(0)  

{  

}  

# 初始化,mStrong(強引用計數)、mWeak(弱引用計數)、mBase(管理的Refbase)

另外,和構造函數一起的還有

    void addStrongRef(const void* /*id*/) { }

    void removeStrongRef(const void* /*id*/) { }

    void addWeakRef(const void* /*id*/) { }

    void removeWeakRef(const void* /*id*/) { }

    void printRefs() const { }

    void trackMe(bool, bool) { }

它們什麼都沒有做。

 

增加弱引用計數

[html]  

void RefBase::weakref_type::incWeak(const void* id)  

{  

    weakref_impl* const impl = static_cast<weakref_impl*>(this);  

    impl->addWeakRef(id);  

    const int32_t c = android_atomic_inc(&impl->mWeak);  

    LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);  

}  

# 通過原子操作增加mWeak; 

 

減少弱引用計數

[html] 

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

    LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);  

    if (c != 1) return;  

      

    if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {  

        if (impl->mStrong == INITIAL_STRONG_VALUE)  

            delete impl->mBase;  

        else {  

//            LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);  

            delete impl;  

        }  

    } else {  

        impl->mBase->onLastWeakRef(id);  

        if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {  

            delete impl->mBase;  

        }  

    }  

}  

# 通過原子操作減少弱引用計數

# 當弱引用計數為0時,那麼就會根據某種條件delete this或者delete Refbase

我們在減少強引用和弱引用,看到瞭OBJECT_LIFETIME_FOREVER和OBJECT_LIFETIME_WEAK兩個標志位,那麼他們是幹什麼的呢。www.aiwalls.com

OBJECT_LIFETIME_FOREVER可能代碼裡面沒有設置為這個標識符的類,而OBJECT_LIFETIME_WEAK隻有Binder設置瞭,我們從名字上看出,OBJECT_LIFETIME_FOREVER指的是對象的生命是永遠,而OBJECT_LIFETIME_WEAK可能是生命時間按照弱引用計數來決定(一般都是通過強引用計數來決定)。

由於用得很少,所以,這兩個標志位對我們的應用而言沒有多大關系,但是,我們還是看看代碼怎麼特殊處理這兩個標志位。

在decStrong函數中,

        if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {

            delete this;

        }

意思是當標志位不是OBJECT_LIFETIME_WEAK時,才會銷毀對象。也就是如果標志位是OBJECT_LIFETIME_WEAK,那麼如果強引用為0時,也不會銷毀對象,看來銷毀對象的任務交給其他地方執行瞭。

還是看上面的decWeak函數,當標志位是OBJECT_LIFETIME_WEAK時,隻有當標志位不是OBJECT_LIFETIME_FOREVER才會銷毀對象;當標志不是OBJECT_LIFETIME_WEAK時,如果強引用計數也為0,那麼就將對象銷毀,否則,將weakref_impl銷毀。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *