Android 強弱指針分析

Android 強弱指針分析

在C C++ 語言中,內存的管理歷來是一個比較難的問題,在java 中內存new 的對象由jvm 虛擬機自動回收。在Android 上面提供了sp 和wp 兩種類型的指針,管理new 出來的對象,能夠自動的回收對象,專業于業務減輕在內存管理上的負擔。

實現對對象的管理通常的做法是使用引用計數,每增加一次引用引用計數增加一,當引用計數為0時,銷毀這個對象。引用計數可以放在對象內部,也可以放在外部。Android的做法是放在對象內部。

在Android 7.0 版本上相關的代碼及位置在:

  • system/core/libutils/RefBase.cpp
  • system/core/include/utils/RefBase.h
  • system/core/include/utils/StrongPointer.h

C++ 11

在C++ 11 中引入了大量的新特性,使一些開發變得簡單。在引用計數的變量上使用了
std::atomic模板類:template <class T> struct atomic;提供原子操作,在原來的版本上使用的是Android平臺封裝的API。
主要用到兩個API:fetch_add 和fetch_sub,用于加1和減1。

integral fetch_add(integral, memory_order = memory_order_seq_cst) volatile;
integral fetch_add(integral, memory_order = memory_order_seq_cst);
integral fetch_sub(integral, memory_order = memory_order_seq_cst) volatile;
integral fetch_sub(integral, memory_order = memory_order_seq_cst);

用于線程的同步的API,沒有找到具體的資料。

atomic_thread_fence

參考C++11 并發指南六(atomic 類型詳解三 std::atomic (續))

這兩個API的最后一個參數是std::memory_order類型。主要是內存模型參數,可以調整代碼的執行順序,告訴編譯器的優化方法,比如如果GCC 加了O2參數,會對代碼的執行順序做一定的調整,但是在多線程中就會帶來一定的影響,出現錯誤,內存模型參數可以指定編譯器的優化方式,限定多個原子語句的執行順序。

C++11 并發指南七(C++11 內存模型一:介紹)

/*
std::memory_order
C++  Atomic operations library 
Defined in header <atomic>
*/
enum memory_order {
    memory_order_relaxed,
    memory_order_consume,
    memory_order_acquire,
    memory_order_release,
    memory_order_acq_rel,
    memory_order_seq_cst
};

主要用到兩個:

  1. std::memory_order_relaxed:線程內順序執行,線程間隨意。
  2. std::memory_order_seq_cst:多線程保持順序一致性,像單線程一樣的執行。

參考:
std::memory_order

這段內容據說完全搞懂的全球屈指可數。

主要的類

1. RefBase

需要能夠自動管理內存的對象都要繼承這個類,在RefBase內部有int 型的引用計數。實際是通過weakref_impl類型的mRefs管理。

RefBase::RefBase() : mRefs(new weakref_impl(this))
{
}

通過 ==void incStrong(const void* id) const== 函數增加引用計數,

  1. refs->incWeak(id); 增加弱引用計數。
  2. 增加強引用計數,如果 const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
    返回值c 為初始值INITIAL_STRONG_VALUE,執行onFirstRef。onFirstRef 函數體為空,可以重載做一些初始化工作。

通過 ==void decStrong(const void* id) const== 減少引用計數。

  1. 減少強引用計數 const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);
  2. 如果從c==1, 先做一些清理工作:onLastStrongRef 接著刪除 delete this
  3. 如果不為1,refs->decWeak(id);
void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->incWeak(id);
    
    refs->addStrongRef(id);
    const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
   
    if (c != INITIAL_STRONG_VALUE)  {
        return;
    }

    int32_t old = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
            std::memory_order_relaxed);

    refs->mBase->onFirstRef();
}

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);
    const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);

    if (c == 1) {
        std::atomic_thread_fence(std::memory_order_acquire);
        refs->mBase->onLastStrongRef(id);
        int32_t flags = refs->mFlags.load(std::memory_order_relaxed);
        if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
            delete this;
            // Since mStrong had been incremented, the destructor did not
            // delete refs.
        }
    }

    refs->decWeak(id);
}

2. RefBase::weakref_type

RefBase::weakref_type 主要定義了兩個函數:incWeak, decWeak,操作弱引用計數。

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 = impl->mWeak.fetch_add(1,std::memory_order_relaxed);
    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}


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

    int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
    if ((flags&OBJECT_LIFETIME_MASK) == 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.load(std::memory_order_relaxed)
                == 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 {
        // This is the OBJECT_LIFETIME_WEAK case. The last weak-reference
        // is gone, we can destroy the object.
        impl->mBase->onLastWeakRef(id);
        delete impl->mBase;
    }
}

==需要注意的是在執行delete 是使用了mFlags 這個變量,在下邊可以看到這個變量的定義。==

3. RefBase::weakref_impl

RefBase::weakref_impl 繼承自RefBase::weakref_type 真實的引用計數使用RefBase的內部類RefBase::weakref_impl管理, 有四個內部變量:mStong mWeak mBase, mFlags. mStrong 和sp 配合,負責強引用計數;mWeak 和wp 配合,負責弱引用計數。

class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
    std::atomic<int32_t>    mStrong;
    std::atomic<int32_t>    mWeak;
    RefBase* const          mBase;
    std::atomic<int32_t>    mFlags;
}

// mFlags定義
// OBJECT_LIFETIME_STRONG 為默認值,對象以強引用計數管理生命周期
// OBJECT_LIFETIME_WEAK             對象以弱引用計數管理生命周期 

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

4. sp 為強指針

負責強引用計數管理,內部有m_ptr 指針保存RefBase對象,重載了 “=”操作符,調用m_ptr的==incStrong==操作引用計數+1, 析構的時候調用==decStrong== -1.

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

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

5. wp 是弱指針

負責對象之間的解引用。如果子類保存有父指針,父類保存有子指針,在析構的時候子類先析構,但是父類保有子類的引用,導致引用計數不為0,無法刪除子類;然后父類析構,子類保有父類的引用計數,父類也無法刪除,這時候需要使用wp避免出現這種情況。和sp 一樣 wp重載了 操作符“=” 調用 incWeak, 在析構的時候 decWeak。
在RefBase 里面有兩個變量mStrong, mWeak 分別保存強弱引用計數,只要強引用計數為0,強制delete。

舉個例子:
我們定義兩個類A B, 后析構的B使用wp類型的指針保存A,在析構的時候如果弱引用類型不為0,只要強引用類型為0,強制delete。A先析構,強引用類型為0,軟引用類型為1,強制delete, 這樣B的強引用類型也變為1,B析構的時候執行完del 后強引用類型為0,delete

template<typename T>
wp<T>& wp<T>::operator = (const wp<T>& other)
{
    weakref_type* otherRefs(other.m_refs);
    T* otherPtr(other.m_ptr);
    if (otherPtr) otherRefs->incWeak(this);
    if (m_ptr) m_refs->decWeak(this);
    m_ptr = otherPtr;
    m_refs = otherRefs;
    return *this;
}

template<typename T>
wp<T>::~wp()
{
    if (m_ptr) m_refs->decWeak(this);
}

6. 強弱指針的對比

  1. 通過類圖可以發現,強指針實現了 “.” "->" 操作符的重載,因此sp 可以直接方位類成員,而wp 卻不能,
  2. 但是wp 可以轉化為sp
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;
}

具體的類圖如下:


Android 指針類圖
Android 指針類圖

二 移植到PC

為了編譯研究測試代碼,把這是三個文件移植到PC環境下。Andrioid7.0代碼針對C++ 11 做了修改,在API的跨平臺編譯上做的非常好,沒什么大的改動,注釋掉部分Android的Log 代碼就編譯通過了。在這里也贊一下 C++ 11。平臺為MAC,IDE為CLion 2016.3,編譯使用CMake。

code

三 LightRefBase

在不考慮類互相引用的情況下,引用計數比較簡單,Android提供了LightRefBase模板類
內部采用 mutable std::atomic<int32_t> mCount; 保存引用計數。

template <class T>
class LightRefBase
{
public:
    inline LightRefBase() : mCount(0) { }
    inline void incStrong(__attribute__((unused)) const void* id) const {
        mCount.fetch_add(1, std::memory_order_relaxed);
    }
    inline void decStrong(__attribute__((unused)) const void* id) const {
        if (mCount.fetch_sub(1, std::memory_order_release) == 1) {
            std::atomic_thread_fence(std::memory_order_acquire);
            delete static_cast<const T*>(this);
        }
    }
    //! DEBUGGING ONLY: Get current strong ref count.
    inline int32_t getStrongCount() const {
        return mCount.load(std::memory_order_relaxed);
    }

    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 std::atomic<int32_t> mCount;
};

最開始的測試代碼如下:

class LightRefBaseTest: public LightRefBase<LightRefBaseTest>{
public:
    LightRefBaseTest(){std::cout << "Hello, LightRefBaseTest!" << std::endl;};
    ~LightRefBaseTest(){std::cout << "Hello, ~LightRefBaseTest()!" << std::endl;};
};


int main() {
    std::cout << "Hello, World!" << std::endl;
    LightRefBaseTest lightTest;
    return 0;
}

/*
結果:

Hello, World!
Hello, LightRefBaseTest!
Hello, ~LightRefBaseTest()!

Process finished with exit code 0
*/

修改下LightRefBaseTest lightTest 為:

int main() {
    std::cout << "Hello, World!" << std::endl;
    LightRefBaseTest* lightTest = new LightRefBaseTest();
    return 0;
}

/*
結果 LightRefBaseTest沒有析構:

Hello, World!
Hello, LightRefBaseTest!

Process finished with exit code 0
*/

再修改下,使用sp 指針,LightRefBaseTest又析構了:

int main() {
    std::cout << "Hello, World!" << std::endl;
    sp<LightRefBaseTest> sp1 = new LightRefBaseTest();
    return 0;
}

/*
看下結果,LightRefBaseTest析構了:

Hello, World!
Hello, LightRefBaseTest!
Hello, ~LightRefBaseTest()!

Process finished with exit code 0
*/

看下互相引用的情況:

class LightRefBaseTest2;

class LightRefBaseTest: public LightRefBase<LightRefBaseTest>{
public:
    LightRefBaseTest(){std::cout << "Hello, LightRefBaseTest!" << std::endl;};
    ~LightRefBaseTest(){std::cout << "Hello, ~LightRefBaseTest()!" << std::endl;};
    void setPointer(sp<LightRefBaseTest2> pointer){mPointer = pointer;};
private:
    sp<LightRefBaseTest2>  mPointer;
};


class LightRefBaseTest2: public LightRefBase<LightRefBaseTest>{
public:
    LightRefBaseTest2(){std::cout << "Hello, LightRefBaseTest2!" << std::endl;};
    ~LightRefBaseTest2(){std::cout << "Hello, ~LightRefBaseTest2()!" << std::endl;};
    void setPointer(sp<LightRefBaseTest> pointer){mPointer = pointer;};

private:
    sp<LightRefBaseTest>  mPointer;
};

int main() {
    std::cout << "Hello, World!" << std::endl;
//    LightRefBaseTest* lightTest = new LightRefBaseTest();
    sp<LightRefBaseTest> sp1 = new LightRefBaseTest();
    sp<LightRefBaseTest2> sp2 = new LightRefBaseTest2();
    sp1->setPointer(sp2);
    sp2->setPointer(sp1);

    return 0;
}
/* 兩個類都沒有析構。LightRefBaseTest析構的時候由于LightRefBaseTest2持有它的引用,導致不能夠調用delete, 同理LightRefBaseTest2也不能夠析構
Hello, World!
Hello, LightRefBaseTest!
Hello, LightRefBaseTest2!

Process finished with exit code 0
*/

LightRefBase 已經很完美的解決了C++ new 對象的管理問題,但是有一個致命的缺陷,不能解決類之間的相互引用。

wp sp RefBase 配合使用。

image
image

第一種析構: 析構路線圖如圖中 A線 所以

class SubRefBaseTest;
class RefBaseTest: public RefBase{
public:
    RefBaseTest(){std::cout << "Hello, RefBaseTest!" << std::endl;};
    ~RefBaseTest(){std::cout << "Hello, ~RefBaseTest()!" << std::endl;};
    void setPointer(sp<SubRefBaseTest> pointer){
        mPointer = pointer;
    };

private:
    sp<SubRefBaseTest> mPointer;
};

class SubRefBaseTest: public RefBase{
public:
    SubRefBaseTest(){ std::cout << "Hello, SubRefBaseTest!" << std::endl;};
    ~SubRefBaseTest(){std::cout << "Hello, ~SubRefBaseTest()!" << std::endl;};
    void setPointer(sp<RefBaseTest> pointer){
        mPointer = pointer;
    };
private:
    sp<RefBaseTest> mPointer;
};

int main() {
    std::cout << "Hello, World!" << std::endl;
    sp<RefBaseTest>  refBaseTest = new RefBaseTest();
    sp<SubRefBaseTest>  subRefBaseTest = new SubRefBaseTest();

    return 0;
}

/*
Hello, World!
Hello, RefBaseTest!
Hello, SubRefBaseTest!
Hello, ~SubRefBaseTest()!
Hello, ~RefBaseTest()!

Process finished with exit code 0
*/
int main() {
    std::cout << "Hello, World!" << std::endl;
    sp<RefBaseTest>  refBaseTest = new RefBaseTest();
    sp<SubRefBaseTest>  subRefBaseTest = new SubRefBaseTest();
    refBaseTest->setPointer(subRefBaseTest);
    subRefBaseTest->setPointer(refBaseTest);
    
    return 0;
}

/* 還是無法析構
Hello, World!
Hello, RefBaseTest!
Hello, SubRefBaseTest!

Process finished with exit code 0
*/
class RefBaseTest: public RefBase{
public:
    RefBaseTest(){std::cout << "Hello, RefBaseTest!" << std::endl;};
    ~RefBaseTest(){std::cout << "Hello, ~RefBaseTest()!" << std::endl;};
    void setPointer(wp<SubRefBaseTest> pointer){
        mPointer = pointer;
    };

private:
    wp<SubRefBaseTest> mPointer;
};

/* 
修改RefBaseTest 引用類型為wp, 正常析構了。
在這里用一個隱式的類型轉化,refBaseTest->setPointer(subRefBaseTest);
將強指針轉為弱指針。看下重載的 “=” 操作符 弱引用加一。 
Hello, World!
Hello, RefBaseTest!
Hello, SubRefBaseTest!
Hello, ~SubRefBaseTest()!
Hello, ~RefBaseTest()!

Process finished with exit code 0
*/

template<typename T>
wp<T>& wp<T>::operator = (const sp<T>& other)
{
    weakref_type* newRefs =
        other != NULL ? other->createWeak(this) : 0;
    T* otherPtr(other.m_ptr);
    if (m_ptr) m_refs->decWeak(this);
    m_ptr = otherPtr;
    m_refs = newRefs;
    return *this;
}

第二種析構

如圖中線路B所示

int main() {
    std::cout << "Hello, World!" << std::endl;

    wp<RefBaseTest> wp1 = new RefBaseTest();
    return 0;
}

C++ 11 的智能指針

C++ 智能指針# Android 強弱指針分析

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容