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參數,會對代碼的執行順序做一定的調整,但是在多線程中就會帶來一定的影響,出現錯誤,內存模型參數可以指定編譯器的優化方式,限定多個原子語句的執行順序。
/*
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
};
主要用到兩個:
- std::memory_order_relaxed:線程內順序執行,線程間隨意。
- std::memory_order_seq_cst:多線程保持順序一致性,像單線程一樣的執行。
這段內容據說完全搞懂的全球屈指可數。
主要的類
1. RefBase
需要能夠自動管理內存的對象都要繼承這個類,在RefBase內部有int 型的引用計數。實際是通過weakref_impl類型的mRefs管理。
RefBase::RefBase() : mRefs(new weakref_impl(this))
{
}
通過 ==void incStrong(const void* id) const== 函數增加引用計數,
- refs->incWeak(id); 增加弱引用計數。
- 增加強引用計數,如果 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== 減少引用計數。
- 減少強引用計數 const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);
- 如果從c==1, 先做一些清理工作:onLastStrongRef 接著刪除 delete this
- 如果不為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. 強弱指針的對比
- 通過類圖可以發現,強指針實現了 “.” "->" 操作符的重載,因此sp 可以直接方位類成員,而wp 卻不能,
- 但是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;
}
具體的類圖如下:

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

第一種析構: 析構路線圖如圖中 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 強弱指針分析