前言
Java 和 C/C++ 的一個(gè)重大區(qū)別景埃,就是它沒(méi)有"指針"的概念普泡,這并不代表 Java 不需要只用指針您访,而是將這個(gè)"超級(jí)武器隱藏了"活逆。Java 以其他更"安全"的形式向開(kāi)發(fā)人員提供了隱形的"指針"精刷,使得用戶既能享受到指針的強(qiáng)大功能,又能盡量避免指針帶來(lái)的問(wèn)題蔗候。
C/C++中常見(jiàn)的指針問(wèn)題
- 指針沒(méi)有初始化
對(duì)指針進(jìn)行初始化是程序員必須養(yǎng)成的良好習(xí)慣怒允,也是指針問(wèn)題中最容易解決和控制的一個(gè)問(wèn)題;
- new 了對(duì)象沒(méi)有及時(shí) delete
動(dòng)態(tài)分配內(nèi)存的對(duì)象锈遥,其實(shí)聲明周期的控制不當(dāng)常常會(huì)引起不少麻煩纫事。如果只有一個(gè)程序員在維護(hù)時(shí),問(wèn)題通常不大所灸,因?yàn)橹灰晕⒘粜木涂梢詫?shí)現(xiàn) new 和 delete 的配套操作丽惶;但是如果一個(gè)大型工程,就很可能會(huì)出現(xiàn)動(dòng)態(tài)分配的內(nèi)存沒(méi)有回收的情況——造成的內(nèi)存泄露問(wèn)題往往是致命的爬立;
- 野指針
假設(shè)1:我們 new 了一個(gè)對(duì)象 A钾唬,并將指針 ptr 指向這個(gè)新的對(duì)象。當(dāng)對(duì) A 使用結(jié)束后懦尝,我們也主動(dòng) delete 了 A知纷,但是唯一沒(méi)做的是將 ptr 指針置空,那么可能出現(xiàn)野指針問(wèn)題陵霉。因此如果有"第三方"視圖用 ptr 來(lái)使用內(nèi)存對(duì)象琅轧,它首先通過(guò)判斷發(fā)現(xiàn) ptr 不為空,就認(rèn)為這個(gè)對(duì)象還是存在的踊挠,其結(jié)果就是導(dǎo)致程序崩潰或是數(shù)據(jù)錯(cuò)誤乍桂;
假設(shè)2:假設(shè) ptr1 和 ptr2 都指向?qū)ο?A冲杀,后來(lái)我們通過(guò) ptr1 釋放了 A 的內(nèi)存空間,并且將 ptr1 也置為 null睹酌;但是 ptr2 并不知道它所指向的內(nèi)存對(duì)象已經(jīng)不存在了权谁,此時(shí)如果 ptr2 來(lái)訪問(wèn) A 也會(huì)導(dǎo)錯(cuò)誤;
Android 智能指針
在開(kāi)發(fā)中經(jīng)常會(huì)使用對(duì)象引用計(jì)數(shù)來(lái)維護(hù)對(duì)象的生命周期憋沿,而該技術(shù)的核心問(wèn)題是由誰(shuí)來(lái)維護(hù)對(duì)象的引用計(jì)數(shù)旺芽,由開(kāi)發(fā)人員維護(hù)顯然既不可靠,又不方便編寫(xiě)和維護(hù)辐啄。而智能指針正是一種可以自動(dòng)維護(hù)對(duì)象引用計(jì)數(shù)的計(jì)數(shù)采章,需要注意的是,智能指針是一個(gè)對(duì)象壶辜,而不是一個(gè)指針悯舟。
現(xiàn)在考慮這樣一個(gè)問(wèn)題:
有兩個(gè)對(duì)象 A 和 B,A 引用了 B砸民,同時(shí) B 也引用了 A抵怎。當(dāng)對(duì)象 A 不再使用時(shí),就可以釋放它所占用的內(nèi)存岭参,但是由于 B 還持有 A 的引用反惕,結(jié)果就是 A 不能被釋放。對(duì)于釋放 B 的資源時(shí)也會(huì)遇到
同樣的問(wèn)題而不能得到釋放冗荸。這個(gè)問(wèn)題也是垃圾回收系統(tǒng)所遇到的經(jīng)典問(wèn)題之一承璃,因?yàn)樗淮沃荒苁占粋€(gè)對(duì)象占用的內(nèi)存(還要看的具體的回收機(jī)制)。
這就要使用一種特殊的智能指針技術(shù)蚌本,該技術(shù)將對(duì)象的引用計(jì)數(shù)分為強(qiáng)引用計(jì)數(shù)和弱引用計(jì)數(shù)兩種盔粹,其中對(duì)象生命周期只受強(qiáng)引用計(jì)數(shù)控制。在使用以上引用計(jì)數(shù)方式時(shí)程癌,一般將有關(guān)聯(lián)的對(duì)象劃分為“父——子”和“子——父”關(guān)系舷嗡。“父”對(duì)象通過(guò)強(qiáng)引用來(lái)引用“子”對(duì)象嵌莉,“子”對(duì)象通過(guò)弱引用來(lái)引用“父”對(duì)象进萄。
以上面的 A 和 B 對(duì)象為例,假設(shè) A 和 B 是“父——子”關(guān)系锐峭,對(duì)象 A 通過(guò)強(qiáng)引用來(lái)引用 B中鼠,而 B 通過(guò)弱引用來(lái)引用 A。當(dāng)對(duì)象 A 不再使用的時(shí)沿癞,由于 B 使用過(guò)弱引用來(lái)引用 A 的援雇,而 對(duì)象的生命周期只受強(qiáng)引用計(jì)數(shù)控制,所以椎扬,A 的生命周期不受 B 的影響惫搏,可以安全釋放具温。在釋放 A 的同時(shí),也會(huì)釋放它對(duì) B 的強(qiáng)引用計(jì)數(shù)筐赔,因此 B 再不需要時(shí)可以被安全釋放铣猩。
由于對(duì)象生命周期只受強(qiáng)引用計(jì)數(shù)控制,因此在 B 想要使用 A 時(shí)茴丰,A 可能已經(jīng)被釋放了达皿,這個(gè)時(shí)候 B 不能直接使用 A 對(duì)象,而是先要成功的將對(duì)象 A 的弱引用計(jì)數(shù)升級(jí)為強(qiáng)引用計(jì)數(shù)贿肩,然后才能使用 A鳞绕;如果引用計(jì)數(shù)升級(jí)失敗,那么 B 就無(wú)法使用 A 了尸曼。
Android 系統(tǒng)設(shè)計(jì)了三種類(lèi)型的 C++ 智能指針,分別為:
- 輕量級(jí)指針:Light Pointer
- 強(qiáng)指針:Strong Pointer
- 弱指針:Weak set_pointer
其中輕量級(jí)指針使用了簡(jiǎn)單引用計(jì)數(shù)萄焦,而強(qiáng)指針和弱指針使用了強(qiáng)引用計(jì)數(shù)和引用計(jì)數(shù)控轿。
Android 提供了基類(lèi) RefBase,用以管理引用數(shù)拂封,所有支持使用強(qiáng)指針和弱指針的類(lèi)必須從 RefBase 派生茬射。設(shè)計(jì)模板類(lèi) sp、wp冒签,用以引用實(shí)際對(duì)象在抛,sp、wp 聲明為棧對(duì)象萧恕,作用域結(jié)束時(shí)刚梭,自動(dòng)釋放,自動(dòng)調(diào)用機(jī)析構(gòu)函數(shù)票唆。因此可以在 sp朴读、wp 的構(gòu)造函數(shù)中,增加引用計(jì)數(shù)走趋,在析構(gòu)函數(shù)中衅金,減少引用計(jì)數(shù)。專(zhuān)門(mén)設(shè)計(jì)的 weakref_impl 類(lèi)簿煌,該類(lèi)是 RefBase 的內(nèi)部類(lèi)氮唯,用來(lái)做真正的引用數(shù)管理,都由 mRef 來(lái)管理姨伟。
Android智能指針的關(guān)系圖:
Android智能指針的源碼位置
android中的智能指針的主要代碼是:RefBase.h惩琉、RefBase.cpp 以及 Pointer.h 這三個(gè)文件,他們分別位于:
RefBase.cpp: /system/core/libutils/RefBase.cpp
RefBase.h: /system/core/include/utils/RefBase.h
StrongPointer.h:/system/core/include/utils/StrongPointer.h
輕量級(jí)指針
這里不多提輕量級(jí)指針授滓,因?yàn)檫@種指針式通過(guò)簡(jiǎn)單引用計(jì)數(shù)技術(shù)來(lái)維護(hù)對(duì)象生命周期的琳水。關(guān)于它只需知道一下 3 點(diǎn):
-
第一點(diǎn)使用它需要繼承 LightRefBase(模板類(lèi))肆糕;
public LightClass: public LightRefBase<LightClass>
第二點(diǎn) LightRefBase 類(lèi)只有一個(gè)成員變量 mCount 用來(lái)描述一個(gè)對(duì)象的引用計(jì)數(shù)值;
第三點(diǎn)需要知道輕量級(jí)指針的實(shí)現(xiàn)類(lèi)和強(qiáng)指針的實(shí)現(xiàn)類(lèi)是同一個(gè)類(lèi) sp在孝。
強(qiáng)指針和弱指針
強(qiáng)指針和弱指針通過(guò)強(qiáng)引用計(jì)數(shù)器和弱引用計(jì)數(shù)器來(lái)維護(hù)對(duì)象的生命周期诚啃。如果一個(gè)類(lèi)的對(duì)象要使用強(qiáng)指針和弱指針,那么就必須從 RefBase 類(lèi)繼承下來(lái)私沮,因?yàn)?RefBase 類(lèi)提供了強(qiáng)引用和弱引用計(jì)數(shù)器始赎。
強(qiáng)指針和弱指針關(guān)系比較密切,他們是配合在一起使用的仔燕。
強(qiáng)指針的實(shí)現(xiàn)原理分析
首先分析 RefBase 類(lèi)的實(shí)現(xiàn)原理造垛,源碼如下:
源碼位置:Android源碼目錄/system/core/include/utils/RefBase.h
class RefBase
{
public:
void incStrong(const void* id) const; // 增加強(qiáng)引用計(jì)數(shù)器的值
void decStrong(const void* id) const; // 減少?gòu)?qiáng)引用計(jì)數(shù)器的值
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); //增加弱引用計(jì)數(shù)器的值
void decWeak(const void* id); //減少弱引用計(jì)數(shù)器的值
// acquires a strong reference if there is already one.
bool attemptIncStrong(const void* id); //增加強(qiáng)引用計(jì)數(shù)器的值
// 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); //減少?gòu)?qiáng)引用計(jì)數(shù)器的值
//! 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(); // 構(gòu)造函數(shù)
virtual ~RefBase(); // 析構(gòu)函數(shù)
//! 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; // 描述對(duì)象引用計(jì)數(shù)
};
RefBase 提供了成員函數(shù) incStrong 和 decStrong 來(lái)維護(hù)他所引用的對(duì)象的引用計(jì)數(shù),這是通過(guò)使用一個(gè) weakref_impl 對(duì)象晰搀,即成員變量 mRefs 來(lái)描述對(duì)象的引用計(jì)數(shù)五辽。
weakref_impl 同時(shí)為類(lèi)提供了強(qiáng)引用和弱引用計(jì)數(shù),源碼如下:
源碼位置:Android源碼目錄 /system/core/libutils/RefBase.cpp
class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
std::atomic<int32_t> mStrong; // 強(qiáng)引用計(jì)數(shù)
std::atomic<int32_t> mWeak; // 弱引用計(jì)數(shù)
RefBase* const mBase; // 引用對(duì)象的地址
std::atomic<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
......
#endif
};
weakref_impl 繼承了 weakref_type 類(lèi)外恕,weakref_type 為 RefBase 內(nèi)部類(lèi)杆逗,它提供了成員函數(shù):incWeak、decWeak鳞疲、attemptIncStrong 和 attemptIncWeak 來(lái)維護(hù)對(duì)象的
強(qiáng)引用計(jì)數(shù)和弱引用計(jì)數(shù)罪郊。weakref_type 只提供了方法接口,具體實(shí)現(xiàn)由 weakref_impl 完成尚洽。
weakref_impl 有兩個(gè)成員變量 mStrong 和 mWeak悔橄,分別描述對(duì)象的強(qiáng)引用計(jì)數(shù)和弱引用計(jì)數(shù)。weakref_impl 的成員變量 mBase 指向了它所引用的對(duì)象的地址腺毫, 成員變量 mFlags 是一個(gè)標(biāo)志值癣疟,用來(lái)描述對(duì)象的生命周期的控制方式。mFlags 的去值范圍為:OBJECT_LIFETIME_STRONG潮酒、OBJECT_LIFETIME_WEAK 或是
OBJECT_LIFETIME_FOREVER争舞,其中 OBJECT_LIFETIME_STRONG 表示對(duì)象的生命周期只受到強(qiáng)引用計(jì)數(shù)的影響;OBJECT_LIFETIME_WEAK 表示對(duì)象的生命周期同時(shí)受到強(qiáng)引用計(jì)數(shù)和弱引用計(jì)數(shù)的影響澈灼;OBJECT_LIFETIME_FOREVER 表示完全不受強(qiáng)引用計(jì)數(shù)和弱引用計(jì)數(shù)的影響竞川。
以上三個(gè)類(lèi)關(guān)系如下:
強(qiáng)指針實(shí)現(xiàn)類(lèi) sp
強(qiáng)指針的實(shí)現(xiàn)類(lèi)為 sp,下面主要分析它的構(gòu)造蛤蟆數(shù)和析構(gòu)函數(shù)叁熔。
sp 源碼如下:
源碼位置:Android源碼目錄/system/core/include/utils/StrongPointer.h
template<typename T>
class sp {
public:
inline sp() : m_ptr(0) { }
sp(T* other);
sp(const sp<T>& other);
sp(sp<T>&& other);
template<typename U> sp(U* other);
template<typename U> sp(const sp<U>& other);
template<typename U> sp(sp<U>&& other);
~sp();
// Assignment
sp& operator = (T* other);
sp& operator = (const sp<T>& other);
sp& operator = (sp<T>&& other);
template<typename U> sp& operator = (const sp<U>& other);
template<typename U> sp& operator = (sp<U>&& other);
template<typename U> sp& operator = (U* other);
//! Special optimization for use by ProcessState (and nobody else).
void force_set(T* other);
// Reset
void clear();
// Accessors
inline T& operator* () const { return *m_ptr; }
inline T* operator-> () const { return m_ptr; }
inline T* get() const { return m_ptr; }
// Operators
COMPARE(==)
COMPARE(!=)
COMPARE(>)
COMPARE(<)
COMPARE(<=)
COMPARE(>=)
private:
template<typename Y> friend class sp;
template<typename Y> friend class wp;
void set_pointer(T* ptr);
T* m_ptr;
};
sp 構(gòu)造函數(shù)
sp 的構(gòu)造函數(shù)如下:
源碼位置:Android源碼目錄/system/core/include/utils/StrongPointer.h
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other) {
if (other)
other->incStrong(this);
}
模塊參數(shù) T 繼承了 RefBase 類(lèi)的子類(lèi)委乌,因此,以上代碼實(shí)際上會(huì)調(diào)用 RefBase 的成員函數(shù) incStrong 來(lái)增加對(duì)象的強(qiáng)引用計(jì)數(shù)荣回,如下所示:
源碼位置:Android源碼目錄/system/core/libutils/RefBase.cp
void RefBase::incStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->incWeak(id); // 增加對(duì)象的弱引用計(jì)數(shù)
refs->addStrongRef(id);
// 增加對(duì)象的強(qiáng)引用計(jì)數(shù)
const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
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;
}
int32_t old = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
std::memory_order_relaxed);
// A decStrong() must still happen after us.
ALOG_ASSERT(old > INITIAL_STRONG_VALUE, "0x%x too small", old);
// 來(lái)通知對(duì)象它被強(qiáng)指針引用了
refs->mBase->onFirstRef();
}
RefBase 的成員變量 mRefs 是在構(gòu)造函數(shù)中初始化的遭贸,如下所示:
RefBase::RefBase()
: mRefs(new weakref_impl(this))
{
}
接著分析 RefBase 的 incStrong 函數(shù),它主要做了三件事:
- 增加對(duì)象的弱引用計(jì)數(shù)心软;
- 增加對(duì)象的強(qiáng)引用計(jì)數(shù)壕吹;
- 如果對(duì)象是第一次被強(qiáng)指針引用著蛙,調(diào)用成員函數(shù) onFirstRef 來(lái)通知對(duì)象,它被強(qiáng)指針引用了耳贬,以便可以執(zhí)行一些業(yè)務(wù)邏輯踏堡。
增加對(duì)象弱引用計(jì)數(shù)是通過(guò)調(diào)用 RefBase 的成員變量 mRefs(也就是 weakref_impl)的成員函數(shù) incWeak 來(lái)實(shí)現(xiàn)的,它是 weakref_type 的子類(lèi)咒劲,函數(shù) incWeak 是從其父類(lèi)繼承下來(lái)的顷蟆,
weakref_type 中 incWeak 代碼如下:
void RefBase::weakref_type::incWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->addWeakRef(id); // 調(diào)試相關(guān),忽略
const int32_t c __unused = impl->mWeak.fetch_add(1,
std::memory_order_relaxed); // 增加弱引用計(jì)數(shù)
ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
這里 this 指針實(shí)際上是指向一個(gè) weakref_impl 對(duì)象腐魂,因此將其轉(zhuǎn)化為 weakref_impl 指針帐偎,接下來(lái)增加它的成員變量 mWeak 的值,即增加對(duì)象的弱引用計(jì)數(shù)蛔屹。
增加了對(duì)向的弱引用后削樊,接著就增加對(duì)象的強(qiáng)引用計(jì)數(shù),也就是增加 mRefs 的成員變量 mStrong 的值兔毒。并返回對(duì)象原來(lái)的強(qiáng)引用計(jì)數(shù)值嫉父,即加一前的值。在 weakref_impl 的構(gòu)造函數(shù)中眼刃,成員
變量 mStrong 的值被初始化為 INITIAL_STRONG_VALUE。INITIAL_STRONG_VALUE 是一個(gè)宏摇肌,其定義如下:
#define INITIAL_STRONG_VALUE (1<<28)
理論上擂红,對(duì)象第一次被強(qiáng)指針引用時(shí),它的強(qiáng)引用計(jì)數(shù)應(yīng)該為 1围小,但是 INITIAL_STRONG_VALUE + 1 的值并不等于 1昵骤,因此,RefBase 類(lèi)的成員函數(shù) incStrong 就需要將它的值調(diào)整為 1 這
是通過(guò)
int32_t old = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,std::memory_order_relaxed);
實(shí)現(xiàn)的肯适。
至此 sp 的構(gòu)造函數(shù)分析完了变秦,它主要做的事就是增加強(qiáng)引用計(jì)數(shù)和弱引用計(jì)數(shù)。
下分析 sp 的析構(gòu)函數(shù)框舔。
sp 析構(gòu)函數(shù)
析構(gòu)函數(shù)代碼如下:
源碼位置:Android源碼目錄/system/core/include/utils/StrongPointer.h
template<typename T>
sp<T>::~sp() {
if (m_ptr)
m_ptr->decStrong(this);
}
m_ptr 所指向的對(duì)象是繼承了 RefBase 的類(lèi)蹦玫,所以這里實(shí)際上調(diào)用了 RefBase 的 decStrong 函數(shù)來(lái)減少對(duì)象的強(qiáng)引用計(jì)數(shù),其實(shí)現(xiàn)如下:
源碼位置:Android源碼目錄/system/core/libutils/RefBase.cpp
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 PRINT_REFS
ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
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;
}
}
refs->decWeak(id);
}
sp 析構(gòu)函數(shù)的主要工作就是減少對(duì)象強(qiáng)引用計(jì)數(shù)和弱引用計(jì)數(shù)刘绣。與其構(gòu)造函數(shù)一樣
const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);
也是返回原來(lái)的強(qiáng)引用計(jì)數(shù)樱溉,即減1前的值,并保存在了變量 c 中纬凤。如果變量 c 的值等于1福贞,也就是說(shuō),此時(shí)沒(méi)有強(qiáng)指針引用這個(gè)對(duì)象了停士,接下來(lái)就可以調(diào)用
refs->mBase->onLastStrongRef(id);
來(lái)執(zhí)行一些業(yè)務(wù)相關(guān)邏輯挖帘,同時(shí)也需要考慮是否需要釋放該對(duì)象完丽。接下來(lái)判斷對(duì)象的生命周期是否只受強(qiáng)引用控制,如果是拇舀,那么就下來(lái)就會(huì)釋放對(duì)象所占用的內(nèi)存逻族,同時(shí)導(dǎo)致 RefBase 的
析構(gòu)函數(shù)被調(diào)用,代碼如下:
源碼位置:Android源碼目錄/system/core/libutils/RefBase.cpp
RefBase::~RefBase()
{
if (mRefs->mStrong.load(std::memory_order_relaxed)
== INITIAL_STRONG_VALUE) {
delete mRefs;
} else {
int32_t flags = mRefs->mFlags.load(std::memory_order_relaxed);
// 生命周期不只是受強(qiáng)引用計(jì)數(shù)控制
if ((flags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {
if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) {
delete mRefs;
}
}
}
// 只受強(qiáng)引用計(jì)數(shù)控制你稚,只釋放 RefBase瓷耙,保留 mRefs
const_cast<weakref_impl*&>(mRefs) = NULL;
}
如果強(qiáng)引用計(jì)數(shù)為初始值,也就是說(shuō)該對(duì)象沒(méi)有被強(qiáng)指針引用過(guò)刁赖,那么釋放成員變量 mRefs(weakref_impl)搁痛;當(dāng)強(qiáng)引用計(jì)數(shù)為 0,但是弱引用不為 0時(shí)宇弛,只能將對(duì)象 RefBase 釋放掉鸡典,而不能將 weakref_impl
對(duì)象 mRefs 釋放掉,因?yàn)檫€有其他的弱指針通過(guò)該 weakref_impl 對(duì)象來(lái)引用實(shí)際對(duì)象枪芒。只有對(duì)象的弱引用計(jì)數(shù)為 0 時(shí)彻况,才可以將 weakref_impl 一起釋放掉。
回到函數(shù) decStrong 中舅踪,接下來(lái)通過(guò)
refs->decWeak(id);
減少弱引用計(jì)數(shù)纽甘。refs 是 weakref_impl 對(duì)象,weakref_impl繼承自 weakref_type抽碌,這里實(shí)際會(huì)調(diào)用 weakref_type 的 decWeak 函數(shù)悍赢,代碼如下:
void RefBase::weakref_type::decWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->removeWeakRef(id);
// 減少弱引用計(jì)數(shù)
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) {
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 {
impl->mBase->onLastWeakRef(id);
delete impl->mBase;
}
}
函數(shù)
impl->mWeak.fetch_sub(1, std::memory_order_release);
會(huì)返回對(duì)象的原弱引用計(jì)數(shù),即減1前的值货徙,并存放在變量 c 中左权,如果 c 不為 1,也就是說(shuō)還有其他弱指針指向該對(duì)象痴颊,因此就不做進(jìn)一步處理赏迟;如果 c 的值等于 1,也就是說(shuō)沒(méi)有其他弱指針指引用
該對(duì)象了蠢棱,同時(shí)也說(shuō)明沒(méi)有強(qiáng)指針引用該對(duì)象了锌杀,這是需要考慮是否釋放掉該對(duì)象。這取決于對(duì)象生命周期的控制方式泻仙,以及該對(duì)象是否被強(qiáng)指針引用過(guò)抛丽。下面分兩種情況討論。
對(duì)象生命周期只受強(qiáng)引用計(jì)數(shù)控制饰豺,即 (flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG 為 true亿鲜,如果此時(shí) impl->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE 也為 true,也就是說(shuō)該對(duì)象沒(méi)有被強(qiáng)指針引用過(guò),那么就可以將該對(duì)象釋放掉蒿柳。如果對(duì)象的生命周期只受強(qiáng)引用計(jì)數(shù)控制饶套,并且也被強(qiáng)指針引用過(guò),那么在該對(duì)象的弱引用計(jì)數(shù)變?yōu)?0 時(shí)垒探,該對(duì)象就已經(jīng)在 RefBase 的成員函數(shù) decStrong 中被釋放掉了妓蛮,因此,接下來(lái)就只釋放其內(nèi)部的引用計(jì)數(shù)器對(duì)象 weakref_impl圾叼。
生命周期受弱引用控制蛤克,,那么接下來(lái)就可以調(diào)用 onLastWeakRef 來(lái)處理一些業(yè)務(wù)相關(guān)邏輯夷蚊,接著將將該對(duì)象釋放掉构挤。
總結(jié)
- 如果一個(gè)對(duì)象的生命周期只受到強(qiáng)引用控制,那么只要它的強(qiáng)引用計(jì)數(shù)值為 0惕鼓,系統(tǒng)就會(huì)釋放掉該對(duì)象;
- 如果一個(gè)對(duì)象的生命周期控制標(biāo)志被設(shè)置為 OBJECT_LIFETIME_WEAK筋现,只有當(dāng)強(qiáng)引用計(jì)數(shù)和弱引用計(jì)數(shù)都是 0時(shí),系統(tǒng)才會(huì)釋放掉這個(gè)對(duì)象箱歧。
弱指針的實(shí)現(xiàn)原理分析
wp 類(lèi)的定義如下矾飞;
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();
// Assignment
wp& operator = (T* other);
wp& operator = (const wp<T>& other);
wp& operator = (const sp<T>& other);
template<typename U> wp& operator = (U* other);
template<typename U> wp& operator = (const wp<U>& other);
template<typename U> wp& operator = (const sp<U>& other);
void set_object_and_refs(T* other, weakref_type* refs);
// promotion to sp
sp<T> promote() const;
// Reset
void clear();
// Accessors
inline weakref_type* get_refs() const { return m_refs; }
inline T* unsafe_get() const { return m_ptr; }
// Operators
COMPARE_WEAK(==)
COMPARE_WEAK(!=)
COMPARE_WEAK(>)
COMPARE_WEAK(<)
COMPARE_WEAK(<=)
COMPARE_WEAK(>=)
inline bool operator == (const wp<T>& o) const {
return (m_ptr == o.m_ptr) && (m_refs == o.m_refs);
}
template<typename U>
inline bool operator == (const wp<U>& o) const {
return m_ptr == o.m_ptr;
}
inline bool operator > (const wp<T>& o) const {
return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
}
template<typename U>
inline bool operator > (const wp<U>& o) const {
return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
}
inline bool operator < (const wp<T>& o) const {
return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
}
template<typename U>
inline bool operator < (const wp<U>& o) const {
return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
}
inline bool operator != (const wp<T>& o) const { return m_refs != o.m_refs; }
template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }
inline bool operator <= (const wp<T>& o) const { return !operator > (o); }
template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }
inline bool operator >= (const wp<T>& o) const { return !operator < (o); }
template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }
private:
template<typename Y> friend class sp;
template<typename Y> friend class wp;
// 指向他所引用的對(duì)象(繼承自 RefBase)
T* m_ptr;
// 用來(lái)維護(hù)對(duì)象的弱引用計(jì)數(shù)
weakref_type* m_refs;
};
需要注意的是,弱指針與強(qiáng)指針有一個(gè)很大的區(qū)別呀邢,就是弱指針不可以直接操作他所引用的對(duì)象洒沦,因?yàn)樗玫膶?duì)象可能不受弱引用計(jì)數(shù)的控制,即它所引用的對(duì)象可能是一個(gè)無(wú)效的對(duì)象价淌。因此如果需要操作一個(gè)弱指針引用的對(duì)象申眼,那么就需要將這個(gè)弱指針升級(jí)為強(qiáng)指針,這是通過(guò)它的成員函數(shù) promote 實(shí)現(xiàn)的输钩。如果升級(jí)成功,那么就說(shuō)明該弱指針?biāo)玫膶?duì)象還沒(méi)有被銷(xiāo)毀仲智,可以正常使用买乃。
wp 的實(shí)現(xiàn)比較復(fù)雜,重點(diǎn)是理解它的構(gòu)造函數(shù)钓辆、析構(gòu)函數(shù)意以及如何將一個(gè)弱指針升級(jí)為一個(gè)強(qiáng)指針剪验。
首先分析它的構(gòu)造函數(shù)。
wp 構(gòu)造函數(shù)
源碼位置:Android源碼目錄/system/core/include/utils/RefBase.h
template<typename T>
wp<T>::wp(T* other)
: m_ptr(other)
{
if (other) m_refs = other->createWeak(this);
}
實(shí)際上是調(diào)用 RefBase 的成員函數(shù) createWeak 來(lái)增加對(duì)象的弱引用計(jì)數(shù)前联,代碼如下:
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
mRefs->incWeak(id);
return mRefs;
}
RefBase 的成員變量 mRefs 指向 weakref_impl 對(duì)象功戚,它的成員方法 incWeak 就是增加實(shí)際引用對(duì)象的弱引用計(jì)數(shù),最后將 mRefs 返回似嗤。
wp 析構(gòu)函數(shù)
源碼位置:Android源碼目錄/system/core/include/utils/RefBase.h
template<typename T>
wp<T>::~wp()
{
if (m_ptr) m_refs->decWeak(this);
}
就是調(diào)用 它的成員變量 m_refs 的成員函數(shù) decWeak 來(lái)減少弱引用計(jì)數(shù)啸臀。m_refs指向的是一個(gè) weakref_impl 對(duì)象,因此這里實(shí)際上會(huì)調(diào)用 weakref_impl 的成員函數(shù) decWeak 來(lái)減少對(duì)象的弱引用計(jì)數(shù)。
接下來(lái)重點(diǎn)分析 wp 的成員函數(shù) promote乘粒,該函數(shù)用來(lái)將一個(gè)弱指針升級(jí)為一個(gè)強(qiáng)指針豌注。前面介紹到弱引用不能直接操作它引用的對(duì)象,那么這是如何實(shí)現(xiàn)的呢灯萍?
- wp 沒(méi)有重載 “*” 和 “->”操作符轧铁,因此,我們就不能直接操作它所引用的對(duì)象旦棉。
wp 的成員函數(shù) promote 實(shí)現(xiàn)如下:
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 指向?qū)ο蟮牡刂烦莘纾琺_refs 指向該對(duì)象內(nèi)部的一個(gè)弱引用計(jì)數(shù)器。只有在對(duì)象地址不為NULL的情況下绑洛,才會(huì)調(diào)用它內(nèi)部的弱引用計(jì)數(shù)器對(duì)象的成員函數(shù) attemptIncStrong 來(lái)試圖增加該對(duì)象的強(qiáng)引用計(jì)數(shù)救斑。如果能夠成功增加強(qiáng)引用計(jì)數(shù),那么就可以成功地把一個(gè)弱指針升級(jí)為一個(gè)強(qiáng)指針诊笤。m_refs是一個(gè)類(lèi)型為 weakref_type 的指針系谐,因此接下來(lái)就會(huì)調(diào)用 weakref_type 的成員函數(shù)attemptIncStrong,其實(shí)現(xiàn)如下:
bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
incWeak(id);
weakref_impl* const impl = static_cast<weakref_impl*>(this);
int32_t curCount = impl->mStrong.load(std::memory_order_relaxed);
ALOG_ASSERT(curCount >= 0,
"attemptIncStrong called on %p after underflow", this);
while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
if (impl->mStrong.compare_exchange_weak(curCount, curCount+1,
std::memory_order_relaxed)) {
break;
}
}
if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
if (curCount <= 0) {
decWeak(id);
return false;
}
while (curCount > 0) {
if (impl->mStrong.compare_exchange_weak(curCount, curCount+1,
std::memory_order_relaxed)) {
break;
}
}
if (curCount <= 0) {
decWeak(id);
return false;
}
} else {
if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {
decWeak(id);
return false;
}
curCount = impl->mStrong.fetch_add(1, std::memory_order_relaxed);
if (curCount != 0 && curCount != INITIAL_STRONG_VALUE) {
impl->mBase->onLastStrongRef(id);
}
}
}
impl->addStrongRef(id);
#if PRINT_REFS
ALOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount);
#endif
if (curCount == INITIAL_STRONG_VALUE) {
impl->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
std::memory_order_relaxed);
}
return true;
}
weakref_type 的成員函數(shù) attemptIncStrong 試圖增加目標(biāo)對(duì)象的強(qiáng)引用計(jì)數(shù)器讨跟,但是可能會(huì)增加失敿退(目標(biāo)對(duì)象已經(jīng)被釋放,或者該目標(biāo)對(duì)象不允許使用強(qiáng)指針引用它)晾匠。
在增加強(qiáng)引用計(jì)數(shù)的同時(shí)也會(huì)增加弱引用計(jì)數(shù)茶袒,因此以上函數(shù)首先調(diào)用 incWeak 來(lái)增加弱引用計(jì)數(shù)。wp 的成員變量 m_refs 指向的是一個(gè) weakref_impl 對(duì)象凉馆,接下來(lái)可以安全地將 this 指針
轉(zhuǎn)化為 weakref_impl 指針薪寓,并保存在 impl 中。
一個(gè)弱指針引用的對(duì)象可能處于兩種狀態(tài):
- 該對(duì)象正被其他強(qiáng)指針引用澜共,因此它的強(qiáng)引用計(jì)數(shù)值大于 0向叉,并且不等于 INITIAL_STRONG_VALUE;
- 該對(duì)象沒(méi)用被任何強(qiáng)者針引用嗦董,即它的強(qiáng)引用計(jì)數(shù)值小于等于 0母谎,或是等于 INITIAL_STRONG_VALUE。
先分析第一種情形京革,由于它的強(qiáng)引用計(jì)數(shù)值大于 0奇唤,也就是說(shuō)這時(shí)候?qū)ο笠欢ㄊ谴嬖诘模虼丝梢园踩膶⑷踔羔樕?jí)為強(qiáng)指針匹摇,并將對(duì)象的強(qiáng)引用計(jì)數(shù)加 1咬扇。
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 (impl->mStrong.compare_exchange_weak(curCount, curCount+1,
std::memory_order_relaxed)) {
break;
}
}
compare_exchange_weak 可以保證原子性,也會(huì)出現(xiàn)增加強(qiáng)引用計(jì)數(shù)失敗的情況廊勃,在調(diào)用函數(shù) compare_exchange_weak 前懈贺,其他線程正在修改 curCount 的值就會(huì)造成這種情況,通過(guò) while 循環(huán)來(lái)重新執(zhí)行該操作。
第二種情況比較復(fù)雜隅居,此時(shí)對(duì)象可能存在也可能不存在钠至,要進(jìn)一步判斷。
-
如果 (flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG 為 true胎源,也就是說(shuō)該對(duì)象生命周期只受強(qiáng)引用計(jì)數(shù)控制棉钧。
- 如果 curCount <= 0 也就是說(shuō)該對(duì)象不存在,那么減少之前增加的弱引用計(jì)數(shù)涕蚤,并返回 false宪卿;
- 如果 curCount > 0,實(shí)際上就是 curCount == INITIAL_STRONG_VALUE万栅,此時(shí)由于該對(duì)象生命周期只受強(qiáng)引用計(jì)數(shù)控制佑钾,而此時(shí)該對(duì)象又沒(méi)有被任何強(qiáng)指針引用過(guò),那么它必然不會(huì)被釋放烦粒,此時(shí)可以安全的將其升級(jí)為強(qiáng)指針休溶。
如果該對(duì)象的生命周期受弱引用計(jì)數(shù)影響,那么就說(shuō)明該對(duì)象肯定是存在的扰她,因?yàn)楝F(xiàn)在正有一個(gè)弱指針在引用該對(duì)象兽掰。但是還要進(jìn)一步調(diào)用函數(shù) onIncStrongAttempted 來(lái)確認(rèn)對(duì)象是否允許強(qiáng)指針引用它。如果為 true徒役,那么就可以成功升級(jí)為強(qiáng)指針孽尽。
onIncStrongAttempted 實(shí)現(xiàn)如下:
bool RefBase::onIncStrongAttempted(uint32_t flags, const void* /*id*/)
{
return (flags&FIRST_INC_STRONG) ? true : false;
}
RefBase 類(lèi)的成員函數(shù)在參數(shù) flags 為 FIRST_INC_STRONG 的情況下允許將一個(gè)指向只受弱引用計(jì)數(shù)影響生命周期的對(duì)象的弱指針升級(jí)為強(qiáng)指針。
到這里關(guān)于只能指針的相關(guān)內(nèi)容的分析就基本完成了忧勿,希望對(duì)你有幫助杉女。
參考文獻(xiàn):
Android系統(tǒng)的智能指針(輕量級(jí)指針横腿、強(qiáng)指針和弱指針)的實(shí)現(xiàn)原理分析