声明
- 在Android系统中经常会遇到一些系统原理性的问题,在此专栏中集中来讨论下。
- 接触Android系统,遇到很多sp、wp相关问题,此篇分析Android系统内的智能指针问题。
- 此篇参考一些博客和书籍,代码基于Android 9.0.0,不方便逐一列出,仅供学习、知识分享。
1 智能指针的设计理念
Java 和 C/C++的一个重大区别,就是它没有“指针”的概念。这并不代表 Java 不需要使用指针,而是被隐藏了起来。如果使用 C/C++开发过一些大型项目,就会知道开发人员最头疼的事情莫过于概率性极低 的死机问题一一而造成系统宕机的根源,往往就是指针异常。所以 Java 以其他更安全的形式向开发人员提供了隐性的指针,使得用户既能享受到指针的强大功能,又能尽量避免指针带来的灾难。
C/C++项目中常见的指针问题可以归纳为:
- 指针没有初始化
- new了对象后没有及时 delete
动态分配的内存对象,其生命周期的控制不当常常会引来不少麻烦。当代码数量不多而且这些动态对象只由一名程序员自己维护时,问题通常不大,因为只要稍微留心就可以实现 new 和 delete 的配套操作;但如果是一个大型工程,由于沟通的不及时或者人员素质的参差不齐,就很可能会出现动态分配的内存没有回收的情况,由此造成的内存泄露问题往往是致命的。
比如 FunctionA()中 new 了一个对象,并作为函数结果返回给调用者,那么原则上这个对象就应该由调用者自行 delete。但是由于某些原因,调用者并没有执行(或者它并不知道要执行)这样的操作,久而久之就会成为系统死机的罪魁祸首。类似的 bug 因为只在某些特定情况下才会发生(比如内存不足),最后表现出来的问题就是偶发性系统宕机一一要彻底找出这类问题的根源很可能会是“大海捞针”。 - 野指针
假设有这样一个场景:我们new了一个对象A,并将指针 ptr 指向这个新对象(即ptr=new A)。当对 A 的使用结束后,我们也主动 delete 了它 (delete A),但唯一没做的是将 ptr 指针置空,那么可能出现什么问题呢? 这就是野指针。因为此时如果有“第三方”试图用 ptr 来使用内存对象 A,它首先通过判断发现 ptr 并不为空,自然而然地就认为这个对象还是存在的,其结果也将导致不可预料的死机。
还有一种情况:假设 ptr1 和 ptr2 都指对象A,后来我们通过 ptr1 释放了 A 的内存空间,并且将 ptrl 也置为 null;但是 ptr2 并不知道它所指向的内存对象已经不存在,此时如果通过 ptr2 来访问A也会导致同样的问题。
1.1 自己设计一个智能指针
既然 C/C++中的指针有如此难以控制,那么如何对它进行改善呢? 在分析 Android 中智能指针的实现原理前,如果让我们自己来设计智能指针,该怎么做呢? 如何设计一个能有效防止以上几个致命弱点的智能指针方案。
- 第一个没有初始化的问题很好解决,只要让智能指针在创建时置为 null 即可。
- 第二个问题是实现 new和 delete 的配套。new了一个对象就需要在某个地方及时地 delete 它。既然是智能的指针,那就意味着它应该是一个默默奉献的辛勤工作者,尽可能自动地帮程序员排忧解难,而不是所有事情都需要程序员手工来完成。
要让智能指针自动判断是否需要回收一块内存空间,应该如何设计? 以下我们称内存对象为 object,称智能指针为 SmartPointer。
- SmartPointer 是个类
首先,SmartPointer 要能记录 object 的内存地址。它的内部应该有一个指针变量指向 object,所以 SmartPointer 是一个类。
class SmartPointer
{
private:
void * m_ptr;//指向object对象
}
- SmartPointer 是一个模板类
智能指针并不是针对某种特定类型的对象设计的,因而一定是模拟类。如下所示:
template <typename T>
class SmartPointer
{
private:
T * m_ptr;//指向object 对象
}
- SmartPointer的构造函数
根据第一个问题的描述,智能指针的构造函数应将 m_ptr 置空。如下所示:
template <typename T>
class SmartPointer
{
inline SmartPointer() : m_ptr(0){ }
private:
T * m_ptr; //将指向 object
}
- 引用计数
这是关键的问题点,智能指针怎么知道应该在什么时候释放一个内存对象呢?答案就是“当不需要的时候”释放一这听起来像一句废话,却引导我们从“什么是需要和不需要”这个入口点来思考问题:假设有一个指针指向这个 object,那就必然代表后者是“被需要”的;直到这个指针解除了与内存对象的关系,我们就可以认为这个内存对象已经“不被需要”。
如果有两个以上的指针同时使用内存对象呢?也好办,只要有一个计数器记录着该内存对象“被需要”的个数即可。当这个计数器递减到零时,就说明该内存对象应该“寿终正寝”了。这就是在很多领域都获得了广泛应用的“引用计数”的概念。
不过又有一个问题点,那就是由谁来管理这个计数器?
1.2 谁来管理这个计数器
1.2.1 计数器由智能指针持有
如果当前只有一个智能指针引用了 object,看上去并没有什么大问题。那么,如果有两个(或以上)的智能指针都需要使用 object 呢? 如下图:
当SmartPointer1 释放了自己与 object 的连接后,mCount-1,这时候发现计数值已经为0,所以根据设计需要去 delete object。然而,此时SmartPointer2 却还在使用 object,显然这将引发致命的问题。
1.2.2 计数器由object自身持有
可以为有计数器需求的内存对象实现一个统一的“父类”,这样只要 object 继承于它就具备了计数的功能。比如下面的范例:
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 类主要提供了两个方法,incStrong 和 decStrong,分别用于增加和减少引用计数值。而且如果当前已经没有人引用内存对象(计数值为 0),它还需要自动释放自己。
那么,incStrong 和 decStrong 这两个函数在什么情况下会被调用呢? 既然是引用计数,当然是在“被引用时”,所以这个工作应该由 SmartPointer 完成。如下所示:
SmartPointer<TYPE> smartP = new object;
当一个智能指针引用了 object 时,其父类中的 incStrong 就会被调用。所以 SmartPointer 必须要重载它的 “=” 运算符(多数情况下只重载 “=” 是不够的,应视具体设计需求而定)。
template <typename T>
class SmartPointer
{
inline SmartPointer() : m_ptr(0){ }
~wp();
SmartPointer& operator = (T* other);//运算符重载
private:
T * m_ptr; //将指向 object
}
template<typename T>
SmartPointer<T>&SmartPointer<T>::operator = (T* other)
{
if(other != null)
{
m_ptr = other; //指向内存对象
other-> incStrong(); //主动增加计数值
}
return *this;
}
//当SmartPointer析构时,调用decStrong释放引用
template<typename T>
wp<T>::~wp()
{
if (m_ptr)m _ptr->decStrong();
}
所以,他们直接的关系图为:
解决了第2个问题,实际上第3个问题也解决了。引用计数器的出现使得放内存对象不再是个别指针的事情,而是对象自己的“内政”,只要有人在使用它,它就不会轻易 delete 自身,也就有效地避免了引用它的指针突然变成“野指针”的情况。
通过上面这个例子,就能很好理解智能指针的实现逻辑了,后面在分析Android 中的智能指针(sp、wp)实现,就更得心应手啦。
2 强指针 sp
看到 sp 并非 SmartPointer 的缩写,而是 StrongPointer 的简写。经过几次系统改版后,sp 这个类已经被完全移出了 RefBase.h 文件。Android 9源码工程中 sp 类的位置在:
system/core/libutils/include/utils/StrongPointer.h
而 wp 以及上一节所讲述的 LightRefBase 都仍在 RefBase.h 中。下面来看看 sp 类中一些重要的接口实现:
template <typename T>
class sp
{
public:
inline sp() : m_ptr(0) { }
sp(T* other); // NOLINT, implicit
sp(const sp<T>& other);
template<typename U> sp(U* other); // NOLINT, implicit
template<typename U> sp(const sp<U>& other); // NOLINT, implicit
~sp();
// Assignment
//重载运算符"="
sp& operator = (T* other);
sp& operator = (const sp<T>& other);
template<typename U> sp& operator = (const 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;
};
它和前一节的 SmartPointer 类在实现上基本是一致的。比如运算符等号的实现为:
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 不为空时,要先撤销它之前指向的内存对象,然后才能赋予其新值。另外,为 sp 分配一个内存对象并不一定要通过操作运算符,它的构造函也是可以的。
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other)
{
if (other) other->incStrong(this);//因为是构造函数,不用担心m_ptr之前已经赋过值
}
这时 m_ptr 就不用先置为 null,可以直接指向目的对象,而析构函数的做法和我们的预想也是一样的:
template<typename T>
sp<T>::~sp()
{
if (m_ptr) m_ptr->decStrong(this);
}
总之,强指针 sp 的实现和前一节的例子基本相同。
3 弱指针 wp
什么是弱指针呢?想象这样一个场景:父对象 parent 指向子对 child,然后子对象又指向父对象,这就存在了循环引用的现象。
比如有如下两个结构体:
struct CDad
{
CChild* myChild;
}
struct CChild
{
CDad* myDad;
}
那么,很可能会产生下图所示的关系。
如果不考虑智能指针,这样的情况并不会导致任何问题。但是在智能指针的场景下,有没有特别要注意的地方呢? 假设这两个类都具有引用计数器的功能:
- CDad 指向了 CChild,所以后者的引用计数器不为零
- CChild 又指向了 CDad,同样也会使它的计数器不为零
这有点类似于操作系统中的死锁一一因为内存回收者发现两者都是处于“被需要”的状态当然不能释放,从而形成了恶性循环。
解决这个矛盾的一种有效方法是采用“弱引用”。具体措施如下:
CDad 使用强指针来引用CChild,而CChild 只使用弱引用来指向父类。 双方规定当强引用计数为0时,不论弱引用是否为 0都可以 delete 自己(在 Android 系统中这个规则是可以调整的后面会介绍)。这样只要有一方得到了释放,就可以成功避免死锁。
但是,会不会导致野指针的问题?比如 CDad 因为强指针计数已经到0,根据规则生命周期就结束了;但此时 CChild 还持有其父类的弱引用,显然如果CChild 此时用这个指针来访问 CDad 将引发致命的问题。鉴于此,我们还要特别规定:
- 弱指针必须先升级为强指针,才能访问它所指向的目标对象。
- 弱指针的主要使命就是解决循环引用的问题。下面具体看看它和强指针有什么区别。
template <typename T>
class wp
{
public:
typedef typename RefBase::weakref_type weakref_type;
inline wp() : m_ptr(0) { }
explicit wp(T* other);
wp(const wp<T>& other);
explicit wp(const sp<T>& other);
template<typename U> explicit wp(U* other);
template<typename U> explicit wp(const sp<U>& other);
template<typename U> explicit 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);
// 升级为强指针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;
T* m_ptr;
weakref_type* m_refs;
};
和 sp 相比,wp 在类定义上有如下重要区别:
- 除了指向目标对象的m_ptr 外,wp 另外有一个 m_refs 指针,类型为 weakref_type。
- 没有重载 ->,* 等运算符。
- 有一个prmote 方法来将 wp 提升为 sp。
- 目标对象的父类不是 LightRefBase,而是 RefBase。
其构造函数为:
template<typename T>
wp<T>::wp(T* other)
: m_ptr(other)
{
if (other) m_refs = other->createWeak(this);
}
与强指针中的构造方法相比:
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other)
{
if (other) other->incStrong(this);//因为是构造函数,不用担心m_ptr之前已经赋过值
}
可见 wp 并没有直接增加目标对象的引用计数值,而是调用了 createWeak 方法。这个函数属于前面提到的 RefBase 类,如下所示:
class RefBase
{
public:
void incStrong(const void* id) const;//增加强引用计数值
void decStrong(const void* id) const;//减少强引用计数值
void forceIncStrong(const void* id) const;
//! DEBUGGING ONLY: Get current strong ref count.
int32_t getStrongCount() const;
class weakref_type //内部类,wp中用到此类
{
public:
RefBase* refBase() const;
void incWeak(const void* id);//增加弱引用计数值
void decWeak(const void* id);//减少弱引用计数值
// acquires a strong reference if there is already one.
bool attemptIncStrong(const void* id);
// 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);
//! 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();
virtual ~RefBase();
//! Flags for extendObjectLifetime()
//以下参数用于修改object生命周期
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 ReferenceMover;
static void moveReferences(void* d, void const* s, size_t n,
const ReferenceConverterBase& caster);
private:
friend class weakref_type;
class weakref_impl;
RefBase(const RefBase& o);
RefBase& operator=(const RefBase& o);
weakref_impl* const mRefs;
};
RefBase 套了一个重要的类 weakref_type,也就是前面 m_refs 指针所属的类型。RefBase 中还有一个类型为 weakref_impl 的成员变量mRefs,从名称上来看,它应该是 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;
#if !DEBUG_REFS //不是debug的情况下
explicit 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 //debug的情况下
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)
{
}
......
#endif //DEBUG_REFS宏结束
}; //RefBase::weakref_impl类结束
从开头的几个变量可以大概猜出 weakref_impl 所做的工作,其中 mStrong 用于强引用计数,mWeak 用于弱引用计数。
宏 DEBUG_REFS 用于指示release 或 debug 版本。可以看到在 release 版本下,addStrongRef,removeStrongRef 等与 Ref 相关的一系列方法都没有具体实现。也就是说,这些方法实际上是用于调试的,在分析时完全可以不用理会。这样一来整个分析也明朗了很多。
Debug 和 Release 版本都将 mStrong 初始化为INITIAL_STRONG_VALUE。这个值的定义如:
#define INITIAL_STRONG_VALUE (1<<28)
而 mWeak 则初始化为 0。
上面#else 到#endif 之间的部分都是 debug 版本要做的工作,所以都可以略过不看。因而上述代码段中并没有引用计数器相关的控制实现,真正有用的代码在类声明的外面。比如我们在 wp构造函数中遇到的 createWeak 函数:
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
mRefs->incWeak(id);
return mRefs;
}
这个函数先增加了 mRefs(也就是 weakref_impl 类型的成员变量)中的引用计数值,然后返回这个mRefs。
其关系如下图所示:
首先 wp中的 m_ptr 还是要指向目标对象(继承自 RefBase)。RefBase 的作用类似于前面讨论的 LightRefBase,只不过它还同时提供了弱引用控制以及其他新的功能。
和 LightRefBase 不同,RefBase 不是直接使用 int 变量来保存引用计数值,而是采用了weakref_type 类型的计数器。因为 ReBase 需要处理多种计数类型。另外,wp 中也同时保存了这个计数器的地址,也就是 wp 中的 m_refs 和 RefBase 中的 mRefs 都指向了计数器。其中 wp 是通过在构造函数中调用目标对象的 createWeak 来获得计数器地址的,而计数器本身是由 RefBase在构造时创建的。
整个 wp 机制看起来很复杂,但与强指针相比实际上只是启用了一个新的计数器 weakref_impl而已,其他所有工作都是围绕如何操作这个计数器而展开的。接下来的重点就是看看这几个类是如何利用新计数器来达到设计目标的。
3.1 incWeak
需要强调的是,虽然 weakref_impl 是 RefBase 的成员变量,但是wp 也可以直接控制它,所以整个逻辑显得稍微有点混乱,估计是为了兼容以前版本而遗留下来的问题。
在createWeak 中,mRefs 通过 incWeak 增加了计数器的弱引用。即:
void RefBase::weakref_type::incWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->addWeakRef(id);//用于调试目的
//真正起作用函数,增加了mWeak 计数器值
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);
}
这个函数真正有用的语句就是const int32_t c __unused = impl->mWeak.fetch_add(1, std::memory_order_relaxed),它增加了mWeak 计数器值,而其他部分都与调试相关,可以略过。这样当 wp 构造完成以后,RefBase 所持有的 weakref_type 计数器中的 mWeak 就为1。后面如果有新的 wp 指向这个目标对象,mWeak 还会持续增加。
而如果是 sp 指向它呢? 在上一小节中分析过,这时 sp 会调用目标对象的 incStrong 方法来增加强引用计数值。当目标对象继承自 RefBase 时,这个函数的实现是:
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);//增加强引用计数值
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 __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();
}
首先剔除与调试相关的语句,真正的操作如下:
refs->incWeak(id);//增加弱指针计数值
const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);//增加强引用计数值
也就是同时增加弱引用和强引用计数值。然后还要判断目标对象是不是第一次被引用,其中的c变量得到的是“增加之前的值”,因而如果等于INITIAL_STRONG_VALUE 就说明是第一次(这时候一方面要回调 onFirstRef通过对象自己被引用,另一方面要对 mStrong 值做调整。)为什么呢?
因为 mStrong 先是被置了INITIAL_STRONG_VALUE = 1<<28,那么当第一次增加时它就是 1<<28+1,所以还要再次减掉INITIAL_STRONG_VALUE 才能得到1。
3.2 decWeak
了解强弱指针对计数器的操作后,再来分析下目标对象在什么情况下会被释放。无非就是考查减少强弱引用时系统所遵循的规则,如下所示是 decStrong 的情况。
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
LOG_ALWAYS_FATAL_IF(BAD_STRONG(c), "decStrong() called on %p too many times",
refs);
if (c == 1) {//减少后强引用计数值已经降为0
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;//删除对象
// The destructor does not delete refs in this case.
}
}
refs->decWeak(id);//减少弱引用计数
}
首先减少 mStrong 计数器,如果发现已经减到0(即c-=1),就需要回调 onLastStrongRef通知这一事件,接着执行删除操作(如果标志是OBJECT_LIFETIME_STRONG的话)。特别要注意,减小强引用计数值时还要同时减小弱引用计数值,即最后的 decWeak(id)。其实现如下:
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);
//先减小 mWeak 计数值,如果发现还不为 0(即 c!=1),就直接返回;
//否则就是弱引用计数值也为0,此时要根据 LIFETIME 标志分别处理。
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;
}
}
上面这段代码是弱引用的处理核心,主要基于以下标志来处理。
enum {
OBJECT_LIFETIME_STRONG = 0x0000,
OBJECT_LIFETIME_WEAK = 0x0001,
OBJECT_LIFETIME_MASK = 0x0001
};
每个目标对象都可以通过以下方法来更改它的引用规则。
void RefBase::extendObjectLifetime(int32_t mode)
{
// Must be happens-before ordered with respect to construction or any
// operation that could destroy the object.
mRefs->mFlags.fetch_or(mode, std::memory_order_relaxed);
}
实际上就是改变了 mFlags 标志值(默认情况下它是0,即OBJECT_LIFETIME_STRONG) 所以当flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG时,即释放规则受强引用控制的情况。
有个问题,既然是强引用控制,那弱引用还要做什么工作呢?
理论上它确实可以直接返回了,不过还有些特殊情况。前面在 inStrong 函数里,我们看到它同时增加了强、弱引用计数值。而增加弱引用时是不会同时增加强引用的,这说明:弱引用的值>=强引用值。当程序走到这里,弱引用计数值一定为 0,而强引用值有两种可能。
- 一种是强引用值为INITIAL_STRONG_VALUE,说明这个目标对象没有被强引用过,也就是说没有办法靠强指针来释放目标,所以需要 delete impl->mBase。
- 另一种就是在有强引用的情况下,此时要 delete impl,而目标对象会由强引用的 decStrong 来释放。
那么,为什么是在这里 delete 这个计数器呢? weakref_impl 既然是由 RefBase 创建的,那么按理来说也应该由它来删除。实际上 RefBase 也想做这个工作,只是力不从心。其析构函数如下:
RefBase::~RefBase()
{
int32_t flags = mRefs->mFlags.load(std::memory_order_relaxed);
// Life-time of this object is extended to WEAK, in
// which case weakref_impl doesn't out-live the object and we
// can free it now.
if ((flags & OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
// It's possible that the weak count is not 0 if the object
// re-acquired a weak reference in its destructor
// 看这里
if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) {
delete mRefs;
}
} else if (mRefs->mStrong.load(std::memory_order_relaxed)
== INITIAL_STRONG_VALUE) {
// We never acquired a strong reference on this object.
LOG_ALWAYS_FATAL_IF(mRefs->mWeak.load() != 0,
"RefBase: Explicit destruction with non-zero weak "
"reference count");
// TODO: Always report if we get here. Currently MediaMetadataRetriever
// C++ objects are inconsistently managed and sometimes get here.
// There may be other cases, but we believe they should all be fixed.
delete mRefs;
}
// For debugging purposes, clear mRefs. Ineffective against outstanding wp's.
const_cast<weakref_impl*&>(mRefs) = NULL;
}
在这种情况下,RefBase 既然是由 decStrong 删除的,那么从上面 decStrong 的执行顺序上来看mWeak 值还不为0,因而并不会被执行。
如果是弱引用控制下的判断规则(即OBJECT_LIFETIME_WEAK),其实和deStrong 中的处理一样,要首先回调通知目标对象这一事件,然后才能执行删除操作。
3.3 小结
分析完了Android 系统中的智能指针源码实现。总结:
- 智能指针分为强指针 sp 和指针 wp 两种。
- 通常情况下目标对象的父类是 RefBase ,这个基类提供了一个 weakref_impl 类型的引用计数器,可以同时进行强弱引用的控制(内部由mStrong 和mWeak 提供计数)。
- 当incStrong增加强引用的,也会增加弱引用。
- 当incWcak时只增加弱引用计数。
- 开发者可以通过 extendObjectLifetime 设置引用计数器的规则,不同规则下对删除目标对象的时机判断也不一样。
- 开发者可以根据程序需求来选择合适的智能指针类型和计数器规则