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

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

导读android

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

为了不出现上述问题,通常的作法就是使用引用计数的方法,每当有一个指针指向了一个new出来的对象时,就对这个对象的引用计数增长1,每当有一个指针再也不使用这个对象时,就对这个对象的引用计数减小1,每次减1以后,若是发现引用计数值为0时,那么,就要delete这个对象了,这样就避免了忘记delete对象或者这个对象被delete以后其它地方还在使用的问题了。面试

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

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

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

[代码1 - LightRefBase源码]less

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;
}

代码输出结果:ui

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中获得了普遍的使用,为代码的安全可用保驾护航。

相关文章
相关标签/搜索