Android系统的计数基类LightRefBase、RefBase与智能指针sp、wp

Android系统的计数基类LightRefBase、RefBase与智能指针sp、wp

导读

Android系统的运行时库层代码是用C++语言来编写的,使用用C++来写代码最容易出错的地方就是指针了,一旦使用不当,轻则造成内存泄漏,重则造成系统崩溃。因此C++程序形成了一个常识:一个new配一个delete,一个malloc配一个free。new的时机非常好判断,当使用者需要新的对象的时候直接new即可。delete的时机有些难判断,有时候你不知道是否还有指针指向这个对象,当前你是否能够delete,亦或是你可能忘记了delete这个对象,冒然的进行delete可能会给系统带来很多野指针。

为了避免出现上述问题,一般的做法就是使用引用计数的方法,每当有一个指针指向了一个new出来的对象时,就对这个对象的引用计数增加1,每当有一个指针不再使用这个对象时,就对这个对象的引用计数减少1,每次减1之后,如果发现引用计数值为0时,那么,就要delete这个对象了,这样就避免了忘记delete对象或者这个对象被delete之后其它地方还在使用的问题了。

但是,如何实现这个对象的引用计数呢?

针对这么容易出错也比较棘手的问题,Android系统为我们提供了计数基类和智能指针,避免出现上述问题,本文将系统地分析Android系统智能指针(计数基类-RefBase、强指针-sp和弱指针-wp)的实现原理。

##1.轻量级计数基类-LightRefBase 首先来看一下系统提供的轻量级计数基类LightRefBase模板,如下代码1所示:

[代码1 - LightRefBase源码]

template <class T>
class LightRefBase
{
public:
    inline LightRefBase() : mCount(0) { }
    inline void incStrong(__attribute__((unused)) const void* id) const {
        android_atomic_inc(&mCount);
    }
    inline void decStrong(__attribute__((unused)) const void* id) const {
        if (android_atomic_dec(&mCount) == 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 renameRefs(size_t n, const ReferenceRenamer& renamer) { }
    inline static void renameRefId(T* ref,
            const void* old_id, const void* new_id) { }

private:
    mutable volatile int32_t mCount;
};

在代码1中,LightRefBase作为所有类的基类,类似于Java中的Object,通过关键变量mCount,实现所有类对象引用关系的计数。这里通过一个C++例子来看,笔者这里使用了Eclipse IDE for C/C++ Developers,将android_atomic_inc(&mCount)依赖于系统给操作变成了++mCount。代码如下所示:

[代码2 - 父类使用LightRefBase引用计数功能]

#include "utils/RefBase.h"
#include "utils/StrongPointer.h"
#include <iostream>
using namespace std;

class A: public LightRefBase<A> {
public:
	A() {
		cout << "Construct object" << endl;
	}
	virtual ~A() {
		cout << "Destory object" << endl;
	}
};

int main(int argc, char** argv) {
	A* pa = new A();
	cout << "0. Light Ref Count: " << pa->getStrongCount() << endl;
	// 引用+1
	sp<A> lpOut = pa;
	cout << "1. Light Ref Count: " << pa->getStrongCount() << endl;
	{
		// 引用+2
		sp<A> lpInner = lpOut;
		cout << "2. Light Ref Count: " << pa->getStrongCount() << endl;
	}
	cout << "3. Light Ref Count: " << pa->getStrongCount() << endl;
	return 0;
}

代码输出结果:

Construct object
0. Light Ref Count: 0
1. Light lpOut Count: 1
2. Light Ref Count: 2
Destory object
3. Light Ref Count: 6357188

代码2中,类A作为LightRefBase类的父类,使用了LightRefBase类模板,通过父类A实例化,将关键变量mCount设为初值0。这里又借助了sp,sp又是什么呢?sp(StrongPointer)也是一个类模版,目前LightRefBase类已经提供了计数变量,但是自身是无法完成计数工作的,需要借助sp来完成计数的工作。通过sp告知当前实际对象的mCount变量增加或者减少。在代码2中,增加作用域,通过sp<A> lpInner = lpOut继续增加mCount变量。

###1.1 sp告知计数增加 在代码2中,可以看到sp<A> lpOut = pa;代码令mCount变量加了1,这个是如何来完成的呢?参考sp中的代码如下:

template<typename T>
sp<T>::sp(T* other) :
	m_ptr(other) {
	if (other)
		other->incStrong(this);
}

通过m_ptr指针指向pa指向的实际对象,other指针调用实际对象mCount变量加1。通过代码1中的cout << "1. Light Ref Count: " << pa->getStrongCount() << endl代码输出mCount的值等于1。这里来看一下实际对象的基类LightRefBase中的incStrong方法。在代码1中可以看到,非常简单通过调用android_atomic_inc完成mCount的值自增1。

###1.2 sp告知计数减少 在代码2的输出中,可以看到跳出作用域的时候,A对象被销毁,输出Destory object,sp的析构函数~sp()也被调用,这里来看一下sp告知计数减少:

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

通过m_ptr调用decStrong完成mCount减少操作。这里来看一下实际对象的基类LightRefBase中的decStrong方法。在代码1中可以看到,非常简单,首先通过通过判断android_atomic_dec对mCount做自己操作,如果发现减1前已经等于1,也就是没有了额外引用通过delete销毁对象。

###1.3 小结

代码1流程示意图

图1-1 代码1流程示意图

在上面介绍的过程中可以得到如下结论:

  1. 不通过sp指向指针,mCount值不变,通过sp<T>定义指针mCount值加一。
  2. 指针之间指向地址相同的地址空间,之间不存在关联性,当实例对象还在的时候任何一个指针(pa、lpOut、lpInner)都可以拿到mCount的值。
  3. sp析构函数被调用,mCount值等于1的时候,实例对象会被销毁,其余指针会变成野指针,参考代码1输出。

##2.复杂型计数基类-RefBase

在计算机科学领域中,垃圾收集(Garbage Collection)功能的系统框架,即提供对象托管功能的系统框架,例如Java应用程序框架,也是采用上述的引用计数技术方案来实现的,然而,简单的引用计数技术不能处理系统中对象间循环引用的情况。考虑这样的一个场景,系统中有两个对象A和B,在对象A的内部引用了对象B,而在对象B的内部也引用了对象A。当两个对象A和B都不再使用时,垃圾收集系统会发现无法回收这两个对象的所占据的内存的,因为系统一次只能收集一个对象,而无论系统决定要收回对象A还是要收回对象B时,都会发现这个对象被其它的对象所引用,因而就都回收不了,这样就造成了内存泄漏。这样,就要采取另外的一种引用计数技术了,即对象的引用计数同时存在强引用和弱引用两种计数,例如,Apple公司提出的Cocoa框架,当父对象要引用子对象时,就对子对象使用强引用计数技术,而当子对象要引用父对象时,就对父对象使用弱引用计数技术,而当垃圾收集系统执行对象回收工作时,只要发现对象的强引用计数为0,而不管它的弱引用计数是否为0,都可以回收这个对象,但是,如果我们只对一个对象持有弱引用计数,当我们要使用这个对象时,就不直接使用了,必须要把这个弱引用升级成为强引用时,才能使用这个对象,在转换的过程中,如果对象已经不存在,那么转换就失败了,这时候就说明这个对象已经被销毁了,不能再使用了。

RefBase要比LightRefBase类要复杂的多,当上述轻量级基类LightRefBase无法满足需求的时候,就需要计数基类RefBase。RefBase在源码中的位置为system/core/include/utils/RefBase.h文件中,代码如下所示:

[代码3 - 复杂型计数基类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
    {
    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()
    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;
};

RefBase类和LightRefBase类一样,都提供了incStrong和decStrong成员函数来操作它的引用计数器。但是在代码3中确找不到mutable volatile int32_t mCount变量来维护引用计数。RefBase提供了强、弱计数机制,通过weakref_impl* const mRefs来提供,具体我们来看一下,强、弱计数是如何提供的。

RefBase类的成员变量mRefs的类型为weakref_impl指针,实现是在system/core/libutils/RefBase.cpp文件中实现的,如下代码4所示:

[代码4 - weakref_impl类]

class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
    volatile int32_t    mStrong;
    volatile int32_t    mWeak;
    RefBase* const      mBase;
    volatile 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

// Debug版本的源代码
.....

#endif
};

为了查看方便,这里我们去掉Debug版本的源代码,如果读者感兴趣可以自行阅读。在代码4中weakref_impl主要提供了四个公有变量,用来维护对象的引用计数。值得关注的主要有如下四点:

  • mStrong表示对象强引用计数,mWeak表示对象弱引用计数。
  • RefBase类中包含weakref_impl类指针mRefs。
  • 同时weakref_impl类也提供了一个成员变量mBase来指向宿主RefBase。
  • mFlags是一个标志位,它指示了维护对象引用计数所使用的策略,它的取值为0。

这里还需要注意一点weakref_impl继承RefBase::weakref_type类,作为一个实现类出现,weakref_type必然会作为接口类,这个对应的接口类的就是RefBase类内部定义的weakref_type类了,这是一种把类的实现与接口定义分离的设计方法。在代码3中,可以看到。RefBase类看起来还是很复杂的,接下来我们画个简单的示意图来看一下RefBase的关系:

RefBase实现类图

图2 RefBase实现类图

上图中可以看出weakref_type作为RefBase的内部类(声明RefBase.h文件中),weakref_impl实现了RefBase::weakref_type(实现在RefBase.cpp文件中)。

提供引用计数器的类RefBase就暂时介绍到这里,这里我们也和LightRefBase基类一样,结合智能指针sp进行分析,稍候通过一个例子来说明复杂型计数基类RefBase的计数功能,但这要比LightRefBase复杂的多。这里我们用一节内容去描述强指针sp和计数基类RefBase配合实现。

##3.强指针-sp

复杂型计数基类RefBase作为所有类的基类,使用sp<T>声明强指针的时候,调用sp模板的构造函数,我们来看一下sp的构造函数实现:

template<typename T>
sp<T>::sp(T* other) :
	m_ptr(other) {
	if (other)
		other->incStrong(this);
}

template<typename T>
sp<T>::sp(const sp<T>& other) :
	m_ptr(other.m_ptr) {
	if (m_ptr)
		m_ptr->incStrong(this);
}

这里传入参数other一定是继承了RefBase类,other调用RefBase的incStrong函数。在图2中的类关系图可以看出,incStrong函数实现在system/core/libutils/RefBase.cpp文件中。代码如下所示:

void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->incWeak(id);
    
    refs->addStrongRef(id);
    const int32_t c = android_atomic_inc(&refs->mStrong);
    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;
    }

    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
    refs->mBase->onFirstRef();
}

成员变量mRefs是在RefBase类的构造函数中创建的,类型为weakref_impl,作为ReBase的关键变量,前面都已经介绍过。在incStrong完成如下工作:

  1. refs->incWeak增加弱引用计数,函数内部调用impl->addWeakRef(id)android_atomic_inc(&impl->mWeak)(其中impl类型为weakref_impl)增加弱引用计数变量mWeak
  2. refs->addStrongRef增加强引用计数,android_atomic_inc(&refs->mStrong)增加强引用计数变量mStrong的值。
  3. 如果发现是首次调用这个对象的incStrong函数,就会调用一个这个对象的onFirstRef函数,让对象有机会在对象被首次引用时做一些处理逻辑。

第三点中c返回的是refs->mStrong加1前的值,如果发现值等于INITIAL_STRONG_VALUE,就说明这个对象的强引用计数是第一次被增加,#define INITIAL_STRONG_VALUE (1<<28),说明refs->mStrong的默认初值是INITIAL_STRONG_VALUE。其中我们需要注意的是android_atomic_inc返回值c为操作前的结果。

android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong)中refs->mStrong这个值加1后等于1<<28 + 1,不等于1,因此,后面要再减去-INITIAL_STRONG_VALUE,于是,refs->mStrong就等于1了,就表示当前对象的强引用计数值为1了,这与这个对象是第一次被增加强引用计数值的逻辑是一致的。

第一点中我们来看一下弱引用计数是如何增加的,这里我们看一下refs->incWeak(id)函数。我们知道,在Release版本中,impl->addWeakRef函数什么也不做,而在Debug版本中,这个函数增加了一个ref_entry对象到了weakref_impl对象的mWeakRefs列表中,表示此weakref_impl对象的弱引用计数被增加了一次。

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 = android_atomic_inc(&impl->mWeak);
    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}

impl->addWeakRef(id)在老罗分析的版本中调用了两次,老罗对此也产生了疑惑,目前6.0版本只调用了一次

这里总结一下强指针类sp在其构造函数里面所做的事情就是分别为目标对象的强引用计数和弱引和计数增加了1。

再来看看强引用指针的析构函数的实现:

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

同样,这里的m_ptr指向的目标对象一定是继承了RefBase类的,因此,这里调用的是RefBase类的decStrong函数,这里来看一下:

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);
    const int32_t c = android_atomic_dec(&refs->mStrong);

    ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
    if (c == 1) {
        refs->mBase->onLastStrongRef(id);
        if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
            delete this;
        }
    }
    refs->decWeak(id);
}
  1. 这里的refs->removeStrongRef函数调用语句是对应前面在RefBase::incStrong函数里的refs->addStrongRef函数调用语句的,在Release版本中,这也是一个空实现函数,真正实现强引用计数减1的操作是android_atomic_dec(&refs->mStrong),如果发现减1前,此对象的强引用计数refs->mStrong为1,就说明从此以后,就再没有地方引用这个目标对象了,这时候,就要看看是否要delete这个目标对象了。

  2. 在强引用计数为0的情况下,如果对象的标志位OBJECT_LIFETIME_MASK被设置了,就说明这个对象的生命周期是受弱引用计数所控制保护的,因此,这时候就不能delete对象,要等到弱引用计数也为0的情况下,才能delete这个对象。

  3. 接下来调用refs->decWeak(id)函数,和前面介绍的refs->incWeak(id)函数相对应,代码如下,调用impl->removeWeakRef(id)它的作用和impl->addWeakRef(id)一致,只有在Debug版本中生效,Release版本中是一个空实现函数。

在第二点中对已经设置OBJECT_LIFETIME_MASK标志位的对象进行了保护,直至弱引用计数变成0的的时候,那么refs->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 = android_atomic_dec(&impl->mWeak);
    ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
    if (c != 1) return;

    if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == 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 == 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 {
        // less common case: lifetime is OBJECT_LIFETIME_{WEAK|FOREVER}
        impl->mBase->onLastWeakRef(id);
        if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
            // this is the OBJECT_LIFETIME_WEAK case. The last weak-reference
            // is gone, we can destroy the object.
            delete impl->mBase;
        }
    }
}

android_atomic_dec函数对mWeak变量进行操作,对减1之前的值c进行判断,如果等于1,开始进入对象回收。首先判断(impl->mFlags&OBJECT_LIFETIME_WEAK)是否等于OBJECT_LIFETIME_STRONG,其中OBJECT_LIFETIME_WEAK和OBJECT_LIFETIME_MASK值是相同的,如果decStrong函数中因为设置了弱引用保护没有delete对象,那么此时这个条件依然不满足,执行else语句,释放对象通过delete impl->mBase实现对象的销毁。mBase是在每次实例化weakref_impl的时候传入的RefBase对象。

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

如果对象没有设置弱引用保护,不受弱引用计数控制,首先判断对象是否没有过一次强计数引用,调用delete impl->mBase释放对象,通常情况impl->mStrong就必然等于0,而不会等于INITIAL_STRONG_VALUE值,除非它一次强引用都没有过。如果impl->mStrong==0就不需要在delete这个对象了,因为前面已经将这个对象delete掉了,重复的操作往往会引起异常。唯一要做的就是把weakref_impl对象delete掉。为什么要delete这个weakref_impl对象呢?这里的weakref_impl对象是在RefBase的构造函数里面new出来的,理论上说应该在在RefBase的析构函数里delete掉这个weakref_impl对象的。在RefBase的析构函数里面,的确是会做这件事情:

RefBase::~RefBase()
{
    if (mRefs->mStrong == INITIAL_STRONG_VALUE) {
        // we never acquired a strong (and/or weak) reference on this object.
        delete mRefs;
    } else {
        // life-time of this object is extended to WEAK or FOREVER, in
        // which case weakref_impl doesn't out-live the object and we
        // can free it now.
        if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {
            // It's possible that the weak count is not 0 if the object
            // re-acquired a weak reference in its destructor
            if (mRefs->mWeak == 0) {
                delete mRefs;
            }
        }
    }
    // for debugging purposes, clear this.
    const_cast<weakref_impl*&>(mRefs) = NULL;
}

但是不要忘记,在这个场景下,目标对象是前面的RefBase::decStrong函数delete掉的,这时候目标对象就会被析构,但是它的弱引用计数值尚未执行减1操作,因此,这里的mRefs->mWeak == 0条件就不成立,于是就不会delete这个weakref_impl对象,因此,就延迟到执行这里decWeak函数时再执行。

如果对象没有过一次强计数引用,也就出现mRefs->mStrong == INITIAL_STRONG_VALUE情况,RefBase析构函数依然会调用delete mRefs删除相应的weakref_impl对象。在else首先判断了mRefs->mFlags如果没有设置了弱引用保护就不再delete这个weakref_impl对象,因为在上面RefBase::weakref_type::decWeak中,我们已经delete这个weakref_impl对象了。只有设置了弱引用保护mRefs->mFlags & OBJECT_LIFETIME_MASK等于1,不等于OBJECT_LIFETIME_STRONG的时候,我们单独delete这个weakref_impl对象。

OBJECT_LIFETIME_FOREVER设置在6.0上不再提供,智能指针不再能够退化成普通指针

目前主要分为两种情况,第一种当不做弱引用保护的时候,强引用计数值为0,则销毁相应的对象;第二种当作弱引用保护的时候,目标对象的强引用计数和弱引用计数都同时为0时,delete这个目标对象。weakref_impl对象是在弱引用计数为0的时候,delete这个weakref_impl对象。

分析到这里,有必要小结一下:

  1. 如果对象的标志位被设置为0,那么只要发现对象的强引用计数值为0,那就会自动delete掉这个对象;
  2. 如果对象的标志位被设置为OBJECT_LIFETIME_WEAK,那么只有当对象的强引用计数和弱引用计数都为0的时候,才会自动delete掉这个对象;
  3. 只有弱引用计数为0的时候,才会deleteweakref_impl对象,但是标志位被设置为0,是在RefBase::weakref_type::decWeak函数中delete,如果标志位被设置为OBJECT_LIFETIME_WEAK是在RefBase析构函数中delete。

##4.弱指针-wp 弱指针所使用的引用计数类与强指针一样,都是RefBase类,因此,这里就不再重复介绍了,我们直接来弱指针的实现:

[代码5 - wp模板]

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();
    
    ......

private:
    template<typename Y> friend class sp;
    template<typename Y> friend class wp;

    T*              m_ptr;
    weakref_type*   m_refs;
};

与强指针类相比,它们都有一个成员变量m_ptr指向目标对象,但是弱指针还有一个额外的成员变量m_refs,它的类型是weakref_type指针,下面我们分析弱指针的构造函数时再看看它是如果初始化的。这里我们需要关注的仍然是弱指针的构造函数和析构函数。

//构造函数
template<typename T>
wp<T>::wp(T* other)
    : m_ptr(other)
{
    if (other) m_refs = other->createWeak(this);
}
// 析构函数
template<typename T>
wp<T>::~wp()
{
    if (m_ptr) m_refs->decWeak(this);
}

构造函数和析构函数分别调用RefBase中的createWeakweakref_impl.decWeak函数:

RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
    mRefs->incWeak(id);
    return mRefs;
}

这里的成员变量mRefs的类型为weakref_impl指针,weakref_impl类的incWeak函数我们在前面已经看过了,它的作用就是增加对象的弱引用计数。在析构函数中,弱指针对象的成员变量m_refs就指向目标对象的weakref_impl对象,最后调用decWeak()函数,它的作用是减少对象的弱引用计数。前面都已经做了介绍,这里不再重复。

分析到这里,弱指针还没介绍完,它最重要的特性我们还没有分析到。前面我们说过,弱指针的最大特点是它不能直接操作目标对象,这是怎么样做到的呢?秘密就在于弱指针类没有重载*和->操作符号,而强指针重载了这两个操作符号。但是,如果我们要操作目标对象,应该怎么办呢,这就要把弱指针升级为强指针了:

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和m_refs来构造一个强指针sp,这里的m_ptr为指目标对象的一个指针,而m_refs则是指向目标对象里面的weakref_impl对象。这里主要分成两个部分:

  1. 判断目标对象是否为空,然后m_refs->attemptIncStrong(&result)生成强引用。
  2. 通过result.set_pointer(m_ptr)将强指针指向目标对象,最后生成的强指针result返回。

首先根据第一点,看一下attemptIncStrong(&result)实现代码:

[代码6 - 弱指针转为强指针]

bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
    incWeak(id);
    
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    int32_t curCount = impl->mStrong;

    ALOG_ASSERT(curCount >= 0,
            "attemptIncStrong called on %p after underflow", this);

    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 (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
            break;
        }
        // the strong count has changed on us, we need to re-assert our
        // situation.
        curCount = impl->mStrong;
    }
    
    if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
        // we're now in the harder case of either:
        // - there never was a strong reference on us
        // - or, all strong references have been released
        if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
            // this object has a "normal" life-time, i.e.: it gets destroyed
            // when the last strong reference goes away
            if (curCount <= 0) {
                // the last strong-reference got released, the object cannot
                // be revived.
                decWeak(id);
                return false;
            }

            // here, curCount == INITIAL_STRONG_VALUE, which means
            // there never was a strong-reference, so we can try to
            // promote this object; we need to do that atomically.
            while (curCount > 0) {
                if (android_atomic_cmpxchg(curCount, curCount + 1,
                        &impl->mStrong) == 0) {
                    break;
                }
                // the strong count has changed on us, we need to re-assert our
                // situation (e.g.: another thread has inc/decStrong'ed us)
                curCount = impl->mStrong;
            }

            if (curCount <= 0) {
                // promote() failed, some other thread destroyed us in the
                // meantime (i.e.: strong count reached zero).
                decWeak(id);
                return false;
            }
        } else {
            // this object has an "extended" life-time, i.e.: it can be
            // revived from a weak-reference only.
            // Ask the object's implementation if it agrees to be revived
            if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {
                // it didn't so give-up.
                decWeak(id);
                return false;
            }
            // grab a strong-reference, which is always safe due to the
            // extended life-time.
            curCount = android_atomic_inc(&impl->mStrong);
        }

        // If the strong reference count has already been incremented by
        // someone else, the implementor of onIncStrongAttempted() is holding
        // an unneeded reference.  So call onLastStrongRef() here to remove it.
        // (No, this is not pretty.)  Note that we MUST NOT do this if we
        // are in fact acquiring the first reference.
        if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {
            impl->mBase->onLastStrongRef(id);
        }
    }
    
    impl->addStrongRef(id);

    // now we need to fix-up the count if it was INITIAL_STRONG_VALUE
    // this must be done safely, i.e.: handle the case where several threads
    // were here in attemptIncStrong().
    curCount = impl->mStrong;
    while (curCount >= INITIAL_STRONG_VALUE) {
        ALOG_ASSERT(curCount > INITIAL_STRONG_VALUE,
                "attemptIncStrong in %p underflowed to INITIAL_STRONG_VALUE",
                this);
        if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE,
                &impl->mStrong) == 0) {
            break;
        }
        // the strong-count changed on us, we need to re-assert the situation,
        // for e.g.: it's possible the fix-up happened in another thread.
        curCount = impl->mStrong;
    }

    return true;
}

前面我们在讨论强指针的时候说到,增加目标对象的强引用计数的同时,也会增加目标对象的弱引用计数,因此,函数在开始的地方首先就是调用incWeak函数来先增加目标对象的引用计数,如果后面试图增加目标对象的强引用计数失败时,会调用decWeak函数来回滚前面的incWeak操作。

这里试图增加目标对象的强引用计数时,分两种情况讨论:

  • 一种情况是此时目标对象正在被其它强指针引用,即它的强引用计数(impl->mStrong)大于0,并且不等于INITIAL_STRONG_VALUE
  • 另一种情况是此时目标对象没有被任何强指针引用,即它的强引用计数(impl->mStrong)小于等于0,或者等于INITIAL_STRONG_VALUE。

第一种情况比较简单,因为这时候说明目标对象一定存在,因此,是可以将这个弱指针提升为强指针的,在这种情况下,只要简单地增加目标对象的强引用计数值就行android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE,&impl->mStrong)

当我们在这里对目标对象的强引用计数执行加1操作时,要保证原子性,因为其它地方也有可能正在对这个目标对象的强引用计数执行加1的操作,前面我们一般是调用android_atomic_inc函数来完成,但是这里是通过调用android_atomic_cmpxchg函数来完成,android_atomic_cmpxchg函数是体系结构相关的函数,在提供了一些特殊的指令的体系结构上,调用android_atomic_cmpxchg函数来执行加1操作的效率会比调用android_atomic_inc函数更高一些。函数android_atomic_cmpxchg是在system/core/include/cutils/Atomic.h文件中定义的一个宏:

int android_atomic_release_cas(int32_t oldvalue, int32_t newvalue,
                               volatile int32_t* addr)
{
    ......
}

它实际执行的函数是android_atomic_release_cas,这个函数的工作原理大概是这样的:如果它发现*addr == oldvalue,就会执行*addr = newvalue的操作,然后返回0,否则什么也不做,返回1。在我们讨论的这个场景中,oldvalue等于curCount,而newvalue等于curCount + 1,于是,在*addr == oldvalue的条件下,就相当于是对目标对象的强引用计数值增加了1。什么情况下*addr != oldvalue呢?在调用android_atomic_release_cas函数之前,oldvalue和值就是从地址addr读出来的,如果在执行android_atomic_release_cas函数的时候,有其它地方也对地址addr进行操作,那么就会有可能出现*addr != oldvalue的情况,这时候就说明其它地方也在操作目标对象的强引用计数了,因此,这里就不能执行增加目标对象的强引用计数的操作了,它必须要等到其它地方操作完目标对象的强引用计数之后再重新执行,这就是为什么要通过一个while循环来执行了。

在代码6的最后部分通过判断curCount是否大于等于INITIAL_STRONG_VALUE,对于第一种情况做出处理,对于已经curCount等于INITIAL_STRONG_VALUE+1的情景进行处理,调用android_atomic_cmpxchg函数将INITIAL_STRONG_VALUE值从curCount中减掉。

if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE,
        &impl->mStrong) == 0) {
    break;
}

第二种情况比较复杂一点,因为这时候目标对象可能还存在,也可能不存了,这要根据实际情况来判断。如果对象没有设置了弱引用保护,也就意味着当强引用计数为0的时候对象将会被销毁。目前处于了目标对象还从来没有被强引用计数过,curCount == INITIAL_STRONG_VALUE,此时弱指针能够被提升为强指针做出的操作是:

while (curCount > 0) {
    if (android_atomic_cmpxchg(curCount, curCount + 1,
            &impl->mStrong) == 0) {
        break;
    }
    // the strong count has changed on us, we need to re-assert our
    // situation (e.g.: another thread has inc/decStrong'ed us)
    curCount = impl->mStrong;
}

为curCount做加1操作。如果设置了弱引用保护,也就意味着强引用计数为0,弱引用计数不为0的时候对象还没有被销毁。使用impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)强引用指针提升为强引用指针,同时强引用计数自加1。

if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {
    // it didn't so give-up.
    decWeak(id);
    return false;
}
// grab a strong-reference, which is always safe due to the
// extended life-time.
curCount = android_atomic_inc(&impl->mStrong);

弱指针提升为强指针,就需要进一步调用目标对象的onIncStrongAttempted来看看是否允许这种情况发生,这又该怎么理解呢?可以这样理解,目标对象的设计者可能本身就不希望这个对象被强指针引用,只能通过弱指针来引用它,因此,这里它就可以重载其父类的onIncStrongAttempted函数,然后返回false,这样就可以阻止弱指针都被提升为强指针。在RefBase类中,其成员函数onIncStrongAttempted默认是返回true的:

bool RefBase::onIncStrongAttempted(uint32_t flags, const void* /*id*/)
{
    return (flags&FIRST_INC_STRONG) ? true : false;
}

分析到这里,弱指针就介绍完了。强指针和弱指针的关系比较密切,同时它们也比较复杂,下面我们再举一个例子来说明强指针和弱指针的用法,同时也验证一下它们的实现原理。

##5.智能指针实例

这里通过一个C++例子来看,笔者这里使用了Eclipse IDE for C/C++ Developers,将android_atomic_inc(&mCount)依赖于系统给操作变成了mCount++android_atomic_dec(&mCount)也是依赖于操作系统,这里变成mCount--。其中ALOG_ASSERT也是采用了简单的替代方案,当然也可以详细打出具体的ALOG_ASSERT信息,如下所示:

#define ALOG_ASSERT(cond, log, ...) LOG_ALWAYS_FATAL_IF(cond, log, ...)
#define LOG_ALWAYS_FATAL_IF(cond, log, ...) \
    ( (cond) \
    ? (void)(printf("", log)) \
    : (void)0 )

我们先来看第一个例子,旨在测试的目标为:

  1. 在实际工程中使用sp强指针和wp弱指针,查看强弱引用计数的变化。
  2. 测试在无弱引用保护下,wp弱指针升强指针的使用。

[代码7 - 测试强指针和弱指针]

#define INITIAL_STRONG_VALUE (1<<28)

class WeightClass : public RefBase{
public:
	void printRefCount() {
		int32_t strong = getStrongCount();
		weakref_type* ref = getWeakRefs();
		cout << "-----------------------" << endl;
		cout << "Strong Ref Count: " << (strong == INITIAL_STRONG_VALUE ? 0 : strong) << endl;
		cout << "Weak Ref Count: " << ref->getWeakCount() << endl;
		cout << "-----------------------" << endl;
	}
};

class StrongClass: public WeightClass {
public:
	StrongClass() {
	}
	virtual ~StrongClass() {
	}
};

void TestStrongClass(StrongClass* pStrongClass) {
	pStrongClass->printRefCount();
	wp<StrongClass> wpOut = pStrongClass;
	pStrongClass->printRefCount();
	{
		sp<StrongClass> spInner = pStrongClass;
		pStrongClass->printRefCount();
	}
	sp<StrongClass> spOut = wpOut.promote();
	cout << "spOut: " << spOut.get()<<endl;
	pStrongClass->printRefCount();
}

int main(int argc, char** argv) {
	cout << "Test Strong Class" << endl;
	StrongClass* pStrongClass = new StrongClass();
	TestStrongClass(pStrongClass);
	return 0;
}

代码输出结果为:

Test Strong Class
-----------------------
Strong Ref Count: 0
Weak Ref Count: 0
-----------------------
-----------------------
Strong Ref Count: 0
Weak Ref Count: 1
-----------------------
-----------------------
Strong Ref Count: 1
Weak Ref Count: 2
-----------------------
spOut: 0
-----------------------
Strong Ref Count: 5313792
Weak Ref Count: 5322744
-----------------------

从结果中可以看出,没有使用智能指针的时候强弱计数都为0,使用wp<StrongClass> wpOut声明指向类实例pStrongClass的时候,弱引用计数从0增加至1,强引用计数还是0不增加。使用sp<StrongClass> spInner声明指向类实例pStrongClass的时候,弱引用计数从1增加至2,强引用计数从0增加至1。跳出强指针作用域的时候,sp<StrongClass> spInner指针调用sp<T>析构函数,智能指针sp执行decStrong函数,同时发现强引用计数为1,释放类实例pStrongClass。然后释放weakref_impl对象。最后结果spOut: 0。

接下来我们在看一个例子,旨在测试的目标为:

  1. 在实际工程中使用sp强指针和wp弱指针,查看强弱引用计数的变化。
  2. 测试在有弱引用保护下,wp弱指针升强指针的使用。

[代码8 - 弱引用保护下的弱升强]

#define INITIAL_STRONG_VALUE (1<<28)

class WeightClass : public RefBase{
public:
	void printRefCount() {
		int32_t strong = getStrongCount();
		weakref_type* ref = getWeakRefs();
		cout << "-----------------------" << endl;
		cout << "Strong Ref Count: " << (strong == INITIAL_STRONG_VALUE ? 0 : strong) << endl;
		cout << "Weak Ref Count: " << ref->getWeakCount() << endl;
		cout << "-----------------------" << endl;
	}
};
class WeakClass: public WeightClass {
public:
	WeakClass() {
		extendObjectLifetime(OBJECT_LIFETIME_WEAK);
	}

	virtual ~WeakClass() {
	}
};

void TestWeakClass(WeakClass* pWeakClass) {
	pWeakClass->printRefCount();
	wp<WeakClass> wpOut = pWeakClass;
	pWeakClass->printRefCount();
	{
		sp<WeakClass> spInner = pWeakClass;
		pWeakClass->printRefCount();
	}
	pWeakClass->printRefCount();
	sp<WeakClass> spOut = wpOut.promote();
	cout << "spOut: " << spOut.get()<<endl;
	pWeakClass->printRefCount();
}

int main(int argc, char** argv) {
	cout << "Test Weak Class: " << endl;
	WeakClass* pWeakClass = new WeakClass();
	TestWeakClass(pWeakClass);
	return 0;
}

代码输出结果为:

Test Weak Class: 
-----------------------
Strong Ref Count: 0
Weak Ref Count: 0
-----------------------
-----------------------
Strong Ref Count: 0
Weak Ref Count: 1
-----------------------
-----------------------
Strong Ref Count: 1
Weak Ref Count: 2
-----------------------
-----------------------
Strong Ref Count: 0
Weak Ref Count: 1
-----------------------
spOut: 0x551500
-----------------------
Strong Ref Count: 1
Weak Ref Count: 2
-----------------------

从结果中可以看出,没有使用智能指针的时候强弱计数都为0,使用wp<WeakClass> wpOut声明指向类实例pWeakClass的时候,弱引用计数从0增加至1,强引用计数还是0不增加。使用sp<WeakClass> spInner声明指向类实例pStrongClass的时候,弱引用计数从1增加至2,强引用计数从0增加至1。跳出强指针作用域的时候,sp<StrongClass> spInner指针调用sp<T>析构函数,智能指针sp执行decStrong函数,同时发现强引用计数为1,但是发现存在弱引用保护,其中mFlag==OBJECT_LIFETIME_WEAK,不释放类实例pWeakClass。出来函数中的作用域发现强引用计数为0,弱引用计数为1。然后调用wpOut.promote()方法,做一个弱升强的操作,此时spOut对象已经存在。查看相应的引用计数:强引用计数为1,弱引用计数为2。

##6.总结

智能指针sp和wp以及计数基类LightRefBase、RefBase的配合实现了对象的及时释放,大大降低了内存泄漏的可能性,在Android底层JNI中得到了广泛的使用,为代码的安全可用保驾护航。

转载于:https://my.oschina.net/feiyangxiaomi/blog/791441

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值