智能指針分為3類為輕量級指針(Light Pointer)、強指針(Strong Pointer)和弱指針(Weak Pointer)。輕量級指針采用的是簡單的計數,可以認為是強指針的簡化版本。在播放器的C++代碼中,特別是涉及到binder通訊的地方有很多智能指針的應用,比如jni中
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
sp是強指針的意思。
基于Android8.1 代碼地址
system/core/include/utils/RefBase.h
system/core/include/utils/StrongPointer.h
system/core/include/libutils/RefBase.cpp
輕量級指針
指針的問題
C/C++指針問題可以歸納為以下2類:
- 野指針
- 指針未初始化,當我們去判斷一個指針是否可用時,往往會判斷指針是否為NULL。未初始化的指針,它有可能指向了一個未知的地址。指針初始化是必須要養成的習慣。
- 將對象delete后,未將指向它的指針設為NULL,這種情況同指針未初始化一樣。
- 另外一種情況是有多個指針指向了對象A,當某個地方將對象A delete后,操作地方地方的指針,就是對一個非法的內存進行操作
- new了對象后沒有delete
動態分配內存是需要程序員主動去刪除的,不然會造成內存泄漏。比如在一個函數中new了一個對象,并將這個對象作為返回值返回。對于一個多人維護的比較復雜工程,如果有這樣的函數,并不一定所以人都會留意去釋放內存,或者改對象需要被多個地方使用到,要在合適的地方去釋放該對象不是那么好處理的。
解決指針問題
智能指針就是為了解決以上問題的,在了解Android 智能指針之前。先來分析下如何解決以上問題,
首先需要有一種能夠自動釋放的方法,而對于程序代碼而言,只有棧內存才會自動去釋放。C++的類,構造函數和析構函數會在創建和銷毀時自動調用到。利用好這兩點是實現智能指針的基礎
智能指針是類
初步設計,智能指針是一個類,類有一個成員指針,能指向任意的object,所以是一個模板類
template <typename T>
class sp {
sp() {}
~sp() {}
private:
T* ptr;
}
對于指針未初始化 只需要在構造進行處理即可
template <typename T>
class sp {
sp() :m_ptr(0){}
~sp() {}
private:
T* m_ptr;;
}
而delete后未置為NULL,還要結合計數問題來考慮,因為會有多個指針指向同一個地址
計數問題
智能指針如何判斷對象的內存不在需要呢,在很多領域有引用計數的概念,及當沒有指針指向該內存時,就可以認為該內存對象不需要了。
那么該如果計數呢,是否能由智能指針來計數?
明顯是很難做到的,如下圖,兩個智能指針的內存空間是獨立的,智能指針持有計數變量,各指針變量之間很難同步
另一種方法是object自己計數,這需要object繼承一個類
當sp類創建時,調用incStrong方法增加計數,當sp釋放時,調用decStrong方法減少計數,當mCount為0時則刪除object內存.
根據使用方法
sp<MediaPlayer>(p);
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
sp類需要重載=運算符和復制構造函數。因此sp類的定義如下
template <typename T>
class sp {
sp() :m_ptr(0){}
~sp() {}
sp(T* other);
sp& operator = (T* other);
private:
T* m_ptr;;
}
sp類在構造函數中調用incStrong增加計數,在析構函數中調用decStrong減少引用計數
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other)
{
if (other) other->incStrong(this);
}
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;
}
template<typename T>
sp<T>::~sp()
{
if (m_ptr) m_ptr->decStrong(this);
}
重載=運算符是考慮同一對象重復賦值的情況。
template <class T>
class LightRefBase
{
public:
inline LightRefBase() : mCount(0) { }
inline void incStrong(__attribute__((unused)) const void* id) const {
__sync_fetch_and_add(&mCount, 1);
}
inline void decStrong(__attribute__((unused)) const void* id) const {
if (__sync_fetch_and_sub(&mCount, 1) == 1) {
delete static_cast<const T*>(this);
}
}
//! DEBUGGING ONLY: Get current strong ref count.
inline int32_t getStrongCount() const {
return mCount;
}
typedef LightRefBase<T> basetype;
protected:
inline ~LightRefBase() { }
private:
friend class ReferenceMover;
inline static void moveReferences(void*, void const*, size_t,
const ReferenceConverterBase&) { }
private:
mutable volatile int32_t mCount;
};
LightRefBase 的decStrong當引用計數為1時,會將自身delete掉。 LightRefBase是Andorid輕量級智能指針的實現方式。而MediaPlayer繼承的是RefBase,會比較復雜,涉及到弱指針轉強指針的問題。不過原理是一樣的。
強指針
強指針 跟輕量級指針 使用的sp類是一樣的,不同的是object繼承的類是RefBase。看一下MediaPlayer的繼承關系
class MediaPlayer : public BnMediaPlayerClient,
public virtual IMediaDeathNotifier
class IMediaDeathNotifier: virtual public RefBase
強指針的原理其實跟輕指針一樣,都是引用計數。
class RefBase
{
public:
void incStrong(const void* id) const;
void decStrong(const void* id) const;
class weakref_type
{
public:
RefBase* refBase() const;
void incWeak(const void* id);
void decWeak(const void* id);
};
weakref_type* createWeak(const void* id) const;
weakref_type* getWeakRefs() const;
typedef RefBase basetype;
protected:
RefBase();
virtual ~RefBase();
//! Flags for extendObjectLifetime()
enum {
OBJECT_LIFETIME_STRONG = 0x0000,
OBJECT_LIFETIME_WEAK = 0x0001,
OBJECT_LIFETIME_MASK = 0x0001
};
private:
weakref_impl* const mRefs;
};
RefBase 嵌套了內部類weakref_type,大部分的工作其實都是weakref_type完成的。RefBase 還有一個成員變量 weakref_impl* const mRefs, 從名字看 weakref_impl 繼承自weakref_type,是它的實現類。
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;
weakref_impl(RefBase* base)
: mStrong(INITIAL_STRONG_VALUE)
, mWeak(0)
, mBase(base)
, mFlags(0)
, mStrongRefs(NULL)
, mWeakRefs(NULL)
, mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
, mRetain(false)
{
}
}
先來了解下強指針會調用到的incStrong和decStrong
void RefBase::incStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->incWeak(id); //增加弱引用計數器
refs->addStrongRef(id); //調試目的,可以不管
//C++11 std::atomic 成員函數
//T fetch_add (T val, memory_order sync = memory_order_seq_cst) volatile noexcept;
//將原子對象的封裝值加 val,并返回原子對象的舊值(適用于整形和指針類型的 std::atomic 特化版本),整個過程是原子的。sync 參數指定內存序:
const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed); //強引用計數加1
if (c != INITIAL_STRONG_VALUE) {
return;
}
//mStrong在構造函數初始化時被賦值為INITIAL_STRONG_VALUE,
//所以第一次增加時還需要
//減去INITIAL_STRONG_VALUE,mStrong的值才為1
int32_t old __unused = 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);
refs->mBase->onFirstRef(); //RefBase為空方法,可由子類繼承實現
}
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); //強引用計數減1
//之前的引用計數只剩下1時,會刪除object內存
if (c == 1) {
std::atomic_thread_fence(std::memory_order_acquire);
refs->mBase->onLastStrongRef(id); //RefBase為空方法,可由子類繼承實現
int32_t flags = refs->mFlags.load(std::memory_order_relaxed);
if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
delete this;
// The destructor does not delete refs in this case.
}
}
refs->decWeak(id); //減少弱引用計數
}
對于強指針,主要關注的是對強引用計數mStrong的操作,原理跟Light Pointer。 這里還有有對弱指針進行操作,在下面再對wp進行介紹
弱指針
強指針的使用會帶來另一個問題,對象互相引用,比如
class A {
B *b;
}
class B {
A *a;
}
如果A 指向了B,B又指向了,則會帶來類似死鎖的問題。解決的方法就是一個應用采用強指針,另一個采用弱指針,當強指針計數為0時,無論弱指針計數是否為0,都可以delete掉該內存。但這又有一個新問題:使用弱指針的一方訪問的對象已經被刪除了,這會導致野指針的問題。所以又做了一項規定弱指針必須先升級為強指針才能訪問其指向的對象
template <typename T>
class wp
{
public:
typedef typename RefBase::weakref_type weakref_type;
inline wp() : m_ptr(nullptr), m_refs(nullptr) { }
wp(T* other); // NOLINT(implicit)
~wp();
// Assignment
wp& operator = (T* other);
void set_object_and_refs(T* other, weakref_type* refs);
// promotion to sp
sp<T> promote() const; //提升為強指針
private:
template<typename Y> friend class sp;
template<typename Y> friend class wp;
T* m_ptr;
weakref_type* m_refs;
};
弱指針有兩個成員指針 m_ptr 指向object,m_refs指向了RefBase中的weakref_type.先看一下其構造函數
template<typename T>
wp<T>::wp(T* other)
: m_ptr(other)
{
m_refs = other ? m_refs = other->createWeak(this) : nullptr;
}
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
mRefs->incWeak(id);
return mRefs;
}
template<typename T>
wp<T>::~wp()
{
if (m_ptr) m_refs->decWeak(this);
}
當一個弱指針被指向某一object時,會調用到createWeak, createWeak會調用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);
LOG_ALWAYS_FATAL_IF(BAD_WEAK(c), "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
// outlives 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) {
// Decrementing a weak count to zero when object never had a strong
// reference. We assume it acquired a weak reference early, e.g.
// in the constructor, and will eventually be properly destroyed,
// usually via incrementing and decrementing the strong count.
// Thus we no longer do anything here. We log this case, since it
// seems to be extremely rare, and should not normally occur. We
// used to deallocate mBase here, so this may now indicate a leak.
ALOGW("RefBase: Object at %p lost last weak reference "
"before it had a strong reference", 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;
}
}
在減弱指針計數后,當c(弱指針計數)不為1,則直接返回. 當c為1,做了一些判斷:
首先 對flag做了判斷,
//! Flags for extendObjectLifetime()
enum {
OBJECT_LIFETIME_STRONG = 0x0000,
OBJECT_LIFETIME_WEAK = 0x0001,
OBJECT_LIFETIME_MASK = 0x0001
};
enum是目標對象的生命周期,每個目標對象可以通過extendObjectLifetime來修改其生命周期(我也不太懂這里)。如果不去修改,默認情況下flag都為OBJECT_LIFETIME_STRONG,故會進到if判斷里面去。
對于這個if判斷的邏輯,這里我也不是很理解,只能直白地說下字面意思,在增加或減少強指針計數時,會同時整加或減少弱指針計數,而在對弱指針計數的操作則不會同時對強指針計數進行操作,所以
弱指針計數 >= 強指針計數
如果impl->mStrong 為INITIAL_STRONG_VALUE,表示從沒被強引用過則不做任何操作,有可能對象之前被弱引用,但是已經被適當地銷毀了。所以不用做任何事情。如果impl->mStrong 不為INITIAL_STRONG_VALUE,即弱指針計數和 強指針計數同時為0,這時候刪除impl, 即RefBase 的 weakref_impl* const mRefs對象。
關于sp和wp,
sp 的incStrong 會同時增加sp和wp計數, wp計數通過incWeak操作
wp 的incWeak 只會增加wp計數
sp 的decStrong 會減小sp計數和wp計數,當sp計數為0時會delete object,object是繼承RefBase的對象,wp計數通過decWeak操作
wp 的decWeak只會減小wp計數, 如果wp計數為0,如果object從未被強引用則不做任何操作(Android 10的邏輯,之前好像會delete impl->mBase,即object對象),否則會delete RefBase 的 weakref_impl* const mRefs對象