android sp wp 解析

原文地址:http://blog.csdn.net/xuqiqiang1993/article/details/68923160



Android系统中,Native层的代码基本都是C++写的,C++跟Java不一样,C++没有垃圾回收机制,C++代码中难于管理new出来对象的释放,稍有不慎就造成内存泄漏。针对此问题,Android中提出了一套类似Java垃圾回收机制的智能指针,采用强指针sp(Strong Pointer)和弱指针wp(Weak Pointer)对目标对象进行应用,实现对象的自动回收。下面我们将从C++的基础知识入手,对Android的智能指针展开逐步的分析。

1      相关基础知识

Android的智能指针,巧妙的运用C++的基础特性,实现对象的自动释放,我们就先来看看都用了C++的什么特性。

1.1        作用域

标记变量的有效范围。从作用域上来看,可以将对象分为全局对象、局部对象、静态全局对象和静态局部对象。

一般来说,局部变量的有效作用域从它的定义点开始,到和定义变量之前最邻近的开括号配对的第一个闭括号,也就是说,作用域由变量所在的最近一对{}括号确定。

  [演示代码1]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. void testScope() {  
  2.        SheepbigSheep; //局部对象  
  3.        {  
  4.               SheepsmallSheep; // 局部对象  
  5.        } // smallSheep的作用域结束  
  6. // bigSheep的作用域结束  

1.2        对象内存空间的分配、释放

从内存分配空间来看,可将对象分为栈对象和堆对象。栈对象在作用域结束后会自动释放,而堆对象需要手动显示的释放。

  [演示代码2]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. void testMemoryMap() {  
  2.        Sheepsheep; // 栈对象,testMemoryMap调用结束后自动释放  
  3.        Sheep*pSheep; // 堆对象,需要手动释放  
  4.        deletepSheep; // 释放pSheep指向的对象  
  5.        pSheep= 0; //将pSheep指向NULL,防止造成野指针  
  6. }  

图1-1是内存空间的分配示意图。


图 1‑1 内存空间的分配、释放

1.3        原子操作函数

定义在system/core/libcutils/Atomic.c,依赖于具体的芯片平台。原子操作函数特点:线程安全,返回旧值。

int32_t android_atomic_add(int32_t increment, volatile int32_t *ptr)

加函数,返回旧值,*ptr = *ptr + increment

int32_t android_atomic_inc(volatile int32_t *addr)

自增操作,返回旧值,*ptr = *ptr + 1

int32_t android_atomic_dec(volatile int32_t *addr)

自减操作, 返回旧值,*ptr = *ptr - 1

int32_t android_atomic_and(int32_t value, volatile int32_t *ptr)

位与操作,返回旧值,*ptr = *ptr & value

int32_t android_atomic_or(int32_t value, volatile int32_t *ptr)

位或操作,返回旧值,*ptr = *ptr | value

int android_atomic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t *ptr)

如果*addr == oldvalue,就会执行*addr = new_value的操作,然后返回0,否则返回1

表 1‑1 Android原子操作函数一览表

1.4        引用计数的原理

栈对象在生命周期,即作用域结束后自动释放,所以我们这里讨论的是堆对象的引用,也就是指针对象。

图1-2是指针引用时,利用引用数管理实际对象释放的原理图。

引用计数的原理很简单,当引用某个对象时,使其引用数+1;引用结束时,使其引用数-1;当引用数为0时,delete掉实际对象。

根据前面的原理,引出两个问题,带着这两个问题,我们来看看Android是怎么实现的。

Ø  怎么管理引用数?

Ø  怎么判断引用开始和结束,怎么增减引用数?

2      Android智能指针原理

Android设计了基类RefBase,用以管理引用数,所有类必须从RefBase派生,RefBase是所有对象的始祖。

设计模板类sp、wp,用以引用实际对象,sp强引用和wp弱引用。sp、wp声明为栈对象,作用域结束时,自动释放,自动调用析构函数。因此,可以在sp、wp的构造函数中,增引用数;在析构函数中,减少引用计数。

专门设计weakref_impl类,该类是RefBase的内部类,用来做真正引用数管理,创建实际对象时,同时创建一个mRefs对象。不管是强引用和弱应用,都由mRefs来管理。

图2-1 展示Android智能指针的关系类图。


图 2‑1 Android智能指针关系图

看了智能指针的实现原理,我们来看看具体的实现是什么样的。

3      智能指针的实现

根据前面的原理,Android设计了强引用sp和弱引用wp,故实际对象的释放,可分为强引用控制和弱引用控制。所谓强引用控制,指的是强引用数mStrong为0时,释放实际对象;弱引用控制,则指的是弱引用数mWeak为0时,才释放实际对象。

下面将结合一些实例,分析具体的实现。我们先来看一段代码实例。

  [代码实例1]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class Sheep: public RefBase { // 羊年,定义Sheep从RefBase派生  
  2. public:  
  3.        Sheep(): RefBase() { }// 可显示调用RefBase的构造,也可以不用  
  4.        virtual~Sheep() { }// 最好声明为virtual,以便从Sheep派生  
  5. };  
  6. void testSheep() {  
  7.        Sheep*pSheep = new Sheep(); // new一个Sheep对象,这个是一个堆对象  
  8.        { // 限定sp的作用域  
  9.               sp<Sheep>spSheep(pSheep); // spSheep是一个栈对象  
  10.               {// 限定wp的作用域  
  11.                      wp<Sheep>wpSheep(pSheep);  
  12.               }//调用wp的析构函数  
  13.        } // 调用sp的析构函数,实际对象pSheep已释放,若再使用pSheep将会出错  
  14. }  


3.1        RefBase构造和mRefs

在实例代码中,我们先定义了一个类Sheep,从RefBase派生,创建了一个实际对象,pSheep 指向实际对象。

在构造Sheep的实际对象时,将调RefBase的构造函数。RefBase的构造函数如下,在构造函数中创建mRefs。

weakref_impl从weakref_type派生,mRefs才是真正的“管家”。

  [system/core/libutils/RefBase.cpp]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. RefBase::RefBase()  
  2.     :mRefs(new weakref_impl(this)) // 真正管理引用计数  
  3. {}  
  4. weakref_impl(RefBase* base)  
  5.     :mStrong(INITIAL_STRONG_VALUE) // 1<<28(268435456),为什么不是0?  
  6.     ,mWeak(0)  
  7.     ,mBase(base) // mBase指向实际对象  
  8.     ,mFlags(0) // 这个标识很重要,指定是强应用控制还是弱引用控制  
  9. {}  

请注意这里的mFlags,默认值为0,可通过修改这个标志来设置是强引用控制,还是弱引用控制,代码如下:

  [system/core/include/utils/RefBase.h]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. enum {  
  2.    OBJECT_LIFETIME_STRONG  = 0x0000,  
  3.    OBJECT_LIFETIME_WEAK    = 0x0001,  
  4.    OBJECT_LIFETIME_MASK    = 0x0001  
  5. };  

mFlags默认为0,即OBJECT_LIFETIME_STRONG,强引用控制。设置为OBJECT_LIFETIME_WEAK时,为弱引用控制。可以通过extendObjectLifetime函数修改,代码如下:

 [system/core/libutils/RefBase.cpp]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. void RefBase::extendObjectLifetime(int32_t mode)  
  2. {  
  3.    android_atomic_or(mode, &mRefs->mFlags);  
  4. }  

3.2        sp构造

接下来,我们创建了一个sp对象spSheep,这是一个栈对象,在其作用域结束后将自动释放,调用sp的析构函数。

  [system/core/include/utils/StrongPointer.h]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. template<typename T>  
  2. sp<T>::sp(T* other)  
  3.         :m_ptr(other) {  
  4.     if(other)  
  5.        other->incStrong(this);  
  6. }  

other指向真正的Sheep对象,在sp的构造函数中,将other赋值给了sp的m_ptr,m-ptr就指向了真正的Sheep对象。

因而,other->incStrong(this),实际就是Sheep的父类RefBase的incStrong函数,代码如下:

  [system/core/libutils/RefBase.cpp]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. void RefBase::incStrong(const void* id) const  
  2. {  
  3.    weakref_impl* const refs = mRefs;  
  4.    refs->incWeak(id); // 调用incWeak函数  
  5.      
  6.    refs->addStrongRef(id); // 由DEBUG_REFS控制,release版本什么也不做  
  7.     constint32_t c = android_atomic_inc(&refs->mStrong); // 强引用数+1,c为旧值  
  8.    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strongref", refs);  
  9.     if (c !=INITIAL_STRONG_VALUE)  { //判断是否是第一次引用  
  10.        return;  
  11.     } // 第一次引用,refs->mStrong为1<<28 +1 (268435457)  
  12.    
  13.    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);  
  14.     // refs->mStrong为1  
  15.    refs->mBase->onFirstRef(); //第一次引用时调用  
  16. }  

在incStrong函数中调用refs 的incWeak函数,incWeak的代码如下:

  [system/core/libutils/RefBase.cpp]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. void RefBase::weakref_type::incWeak(const void*id)  
  2. {  
  3.    weakref_impl* const impl = static_cast<weakref_impl*>(this);  
  4.    impl->addWeakRef(id); // 由DEBUG_REFS控制,release版本什么也不做  
  5.     constint32_t c __unused = android_atomic_inc(&impl->mWeak); //弱引用数+1  
  6.    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weakref"this);  
  7. }  

OK,sp构造完成,增加一次强引用。sp构造完成后,mRefs的强引用数变为1,弱引用数也变为1;第一次强引用时,回调onFirstRef()。

3.3        wp构造

接下来,我们创建了一个wp对象wpSheep,这是一个栈对象,在其作用域结束后将自动释放,调用wp的析构函数。

  [system/core/include/utils/RefBase.h]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. template<typename T>  
  2. wp<T>::wp(T* other)  
  3.     :m_ptr(other)  
  4. {  
  5.     if(other) m_refs = other->createWeak(this);  
  6. }  

other指向真正的Sheep对象,在wp的构造函数中,将other赋值给了wp的m_ptr,m-ptr就指向了真正的Sheep对象。

因而,other-> createWeak (this),实际就是Sheep的父类RefBase的createWeak函数,代码如下:

  [system/core/libutils/RefBase.cpp]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. RefBase::weakref_type* RefBase::createWeak(constvoid* id) const  
  2. {  
  3.    mRefs->incWeak(id); // incWeak函数前面分析过,最终的结果就是弱引用数+1  
  4.     returnmRefs;  
  5. }  

reateWeak时,调用incWeak,最终的影响是弱引用数+1。现在,我们的实例中,强引用数为1,弱引用数为2。

返回值为mRefs,也就是m_refs和mRefs指向同一个weakref_impl对象,而mRefs的mBase指向真正的对象Sheep。因此此处的spSheep和wpSheep都是管理同一个真正的对象。

3.4        wp析构

继续看我们的实例代码,现在wpSheep的作用域结束,将调wp的析构函数,wp析构函数的代码如下:

  [system/core/include/utils/RefBase.h]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. template<typename T>  
  2. wp<T>::~wp()  
  3. {  
  4.     if(m_ptr) m_refs->decWeak(this); // 调用decWeak函数  
  5. }  

在wp的析构函数中调用m_refs的decWeak函数。m_refs和mRef指向同一个weakref_impl对象,decWeak代码如下:

  [system/core/libutils/RefBase.cpp]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. void RefBase::weakref_type::decWeak(const void*id)  
  2. {  
  3.    weakref_impl* const impl = static_cast<weakref_impl*>(this);  
  4.    impl->removeWeakRef(id);  
  5.     constint32_t c = android_atomic_dec(&impl->mWeak); // 弱引用数-1,c为旧值  
  6.    ALOG_ASSERT(c >= 1, "decWeak called on %p too many times",this);  
  7.     if (c !=1) return//c为旧值,判断是否是最后一次弱引用  
  8.     // 记得前面我们说的,mFlags为0,我们并没有改变它  
  9.     if((impl->mFlags&OBJECT_LIFETIME_WEAK) ==  
  10.             OBJECT_LIFETIME_STRONG){  
  11.         // 强引用控制,是否释放实际对象是根据强引用数  
  12.         if(impl->mStrong == INITIAL_STRONG_VALUE) {  
  13.            delete impl->mBase; // 根本就没有强引用引用实际对象,释放实际对象  
  14.         }else {  
  15.            delete impl; // 释放mRefs  
  16.         }  
  17.     } else {  
  18.        impl->mBase->onLastWeakRef(id); //最后一次弱引用时调用  
  19.         if((impl->mFlags&OBJECT_LIFETIME_MASK) ==  
  20.                         OBJECT_LIFETIME_WEAK) {  
  21.            delete impl->mBase; //弱引用控制,释放实际对象  
  22.         }  
  23.     }  
  24. }  

wp析构,情况比较复杂,总的说来做了以下几件事:

Ø  弱引用数减1。

Ø  最后一次弱引用时,强引用控制,释放mRefs,若没有强引用,释放实际对象

Ø  最后一次弱引用时,弱引用控制,释放实际对象

就我们的实例来看,此时,强引用数为1,弱引用数为1,并没有任何释放。

3.5        sp析构

在我们的实例代码中,wp析构完后,sp的作用域也就结束了。此时,会调用sp的析构函数,代码如下:

  [system/core/include/utils/StrongPointer.h]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. template<typename T>  
  2. sp<T>::~sp() {  
  3.     if(m_ptr)  
  4.        m_ptr->decStrong(this);  
  5. }  

在析构函数中调用m_ptr的decStrong函数,m_ptr指向实际对象。此处为Sheep的父类RefBase的decStrong函数,代码如下:

  [system/core/libutils/RefBase.cpp]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. void RefBase::decStrong(const void* id) const  
  2. {  
  3.    weakref_impl* const refs = mRefs;  
  4.     refs->removeStrongRef(id);// 由DEBUG_REFS控制,release版本什么也不做  
  5.     constint32_t c = android_atomic_dec(&refs->mStrong); // 强引用数-1  
  6.    ALOG_ASSERT(c >= 1, "decStrong() called on %p too manytimes", refs);  
  7.     if (c ==1) { // c为旧值,c为1时,即强引用数为0  
  8.        refs->mBase->onLastStrongRef(id); //最后一次强引用结束时调用  
  9.         if((refs->mFlags&OBJECT_LIFETIME_MASK) ==  
  10.                         OBJECT_LIFETIME_STRONG){  
  11.            delete this// 若是强引用控制,释放实际对象,调实际对象的析构函数  
  12.         }  
  13.     }  
  14.    refs->decWeak(id);  

refs的decWeak函数,前面wp析构的时候分析过,这里不再重复。sp析构完成,主要完成以下工作:

Ø  强引用数减1,弱引用数据减1。

Ø  最后一次强引用时,若是强引用控制,释放实际对象,释放mRefs,调用onLastStrongRef函数。

在我们的代码中,此时强引用数为0,弱引用数为0,实际对象的析构函数将被调用,mRefs将被释放。下面我看看实际对象的析构函数。

3.6        RefBase析构

实际对象的析构,先析构RefBase,RefBase的析构函数如下:

  [system/core/libutils/RefBase.cpp]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. RefBase::~RefBase()  
  2. {  
  3.     if(mRefs->mStrong == INITIAL_STRONG_VALUE) {  
  4.        delete mRefs; // 没有强引用引用实际对象,释放mRefs  
  5.     } else {  
  6.         if((mRefs->mFlags & OBJECT_LIFETIME_MASK) !=  
  7.                         OBJECT_LIFETIME_STRONG){  
  8.            if (mRefs->mWeak == 0) {  
  9.                delete mRefs; // 释放mRefs指向的对象  
  10.            }  
  11.         }  
  12.     }  
  13.     // fordebugging purposes, clear this.  
  14.    const_cast<weakref_impl*&>(mRefs) = NULL; // mRefs指向0,避免野指针  
  15. }  

OK,RefBase析构分析完了,在RefBase的析构函数中主要的工作就是释放mRefs指向的weakref_impl的对象。

到此,我们的实例代码分析完成,我们首先构造一个Sheep对象,pSheep指向实际对象。再分别构造一个强引用sp和一个弱引用wp,用以引用实际对象,实际对象的释放就由sp和wp控制,我们并没有显示的释放构造的pSheep指向的实际对象。

我们来看看实例代码1中,对象的构造和析构Log:

  [示例代码1的Log]

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. D/       (13624): Sheep::------------------testSheepstart--------------------------  
  2. D/       (13624): Sheep::Sheep constructor invoked this=0xb6301080  
  3. D/       (13624): Sheep:: No refs, strong count=268435456, weak count=0  
  4. D/       (13624): Sheep::in sp scope ------------  
  5. D/       (13624): Sheep::onFirstRef, object=0xb6301080  
  6. D/        (13624): Sheep:: After strong ref, strongcount=1, weak count=1  
  7. D/       (13624): Sheep::in wp scope ------------  
  8. D/       (13624): Sheep:: After weak ref, strong count=1, weak count=2  
  9. D/       (13624): Sheep::out wp scope ------------  
  10. D/        (13624):Sheep:: release weak ref, strong count=1, weak count=1  
  11. D/       (13624): Sheep::out sp scope ------------  
  12. D/       (13624): Sheep::onLastStrongRef, id=0xbec42884  
  13. D/       (13624): Sheep::Sheep destructor invoked this=0xb6301080  
  14. D/       (13624): Sheep::--------------------testSheepend--------------------------  

3.7        实际对象的状态

通过前面的分析,我们可以绘制出实际对象的状态图,如下如所示:


图 3‑1 实际对象的状态图

4      智能指针的使用

前面我们通过示例代码1,知道了智能指针是怎么管理实际对象的,怎么控制实际对象的释放的。但是我们只是分析了其中的构造函数和析构函数,下面我们将对智能指针做全面的了解。

4.1        RefBase的特性

我们先看看RefBase的类图,如图4-1所示。


图 4‑1 RefBase类图

Ø  所有类须从RefBase派生,只有一个无参构造函数,RefBase析构函数需申明为virtual。

Ø  在构造函数中创建mRefs对象,为weakref_impl类型。

Ø  可以在派生类中通过函数extendObjectLifetime指定是强引用控制,还是弱引用控制,默认为强引用控制。

Ø  在析构函数中,判断是否释放mRefs。

Ø  私有的构造函数和赋值运算重载,不允许子类使用。

Ø  获取实际对象的强引用数getStrongCount

Ø  子类可派生virtual成员函数,获知自身的引用情况。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章