android系统核心机制 基础(01)智能指针wp sp
该系列文章总纲链接:android 系统核心机制基础 系列文章目录
本章关键点总结 & 说明:
以上是本模块的导图,整体概括了智能指针的几个要点,引用计数,弱转强,flag标志意义以及LightRefBase解读。
RefBase、sp和wp解读:
RefBase是Android中所有对象的始祖,类似于Java中的Object。在Android 中,RefBase结合sp和wp,实现了一套通过引用计数的方法来控制对象生命周期的机制。sp和wp的定义:sp是strong pointer,wp是weak pointer。sp和wp的目的,就是为了帮助健忘的程序员回收new出来的内存。 接下来这里以使用实例进行说明与分析。
1 影子对象
//类A从RefBase派生,RefBase是万物的始祖
class A:public RefBase
{//A没有任何自己的功能
}
int main()
{A* pA =new A;{//注意我们的sp,wp对象是在{}中创建的,下面的代码先创建sp,然后创建wpsp<A> spA(A);wp<A> wpA(spA);...//正常其他的使用spA和wpA操作//大括号结束前,先析构wp,再析构sp}
}
@1 这里分析RefBase构造函数。代码如下:
RefBase::RefBase(): mRefs(new weakref_impl(this))
{//mRefs是RefBase的成员变量,类型是weakref_impl,我们暂且叫它影子对象,所以A有一个影子对象
}
mRefs是引用计数管理的关键类,需要进去观察。它是从RefBase的内部类weakref_type中派生出来的。声明如下:
//RefBase.h中weakref_type的声明
typedef typename RefBase::weakref_type weakref_type;
//RefBase.cpp中定义的派生关系
class RefBase::weakref_impl : public RefBase::weakref_type //从RefBase的内部类weakref_type派生
C++的内部类和Java内部类相似,不同点是它需要一个显示的成员指向外部类对象。weakref_impl构造实现如下所示:
weakref_impl(RefBase* base): mStrong(INITIAL_STRONG_VALUE) //强引用计数,初始值为0x1000000,即(1<<28), mWeak(0)//弱引用计数,初始值为0, mBase(base))//该影子对象所指向的实际对象, mFlags(0){}
new了一个A对象后,其实还new了一个RefBase::weakref_impl内部类对象,这里称它为影子对象,A为实际对象。影子对象成员中有两个引用计数:一个强引用,一个弱引用。在构造一个实际对象的同时还会悄悄地构造一个影子对象。
@2 程序继续运行,现在到了
sp<A> spA(A);
sp的构造函数,它的代码如下所示(sp是一个模板类,这里列举最关键sp摸板函数):
template<typename T> //根据程序特点,运行的是这个构造器
sp<T>::sp(T* other) //这里的other就是刚才创建的pA
: m_ptr(other) // sp保存了pA的指针{if (other) other->incStrong(this);//调用pA的incStrong}
...//其他构造器略过
继续分析RefBase的incStrong中,它的代码如下所示:
void RefBase::incStrong(const void* id) const
{weakref_impl* const refs = mRefs;//mRefs就是刚才RefBase构造函数中new出来的内部影子对象refs->incWeak(id);refs->addStrongRef(id);//这句不考虑,Passconst int32_t c = android_atomic_inc(&refs->mStrong);//原子加1操作,并返回旧值。所以c=0x1000000,而mStrong变为0x1000001ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);if (c != INITIAL_STRONG_VALUE) { //如果c不是初始值,则表明这个对象已经被强引用过一次了,所以直接返回return;}android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);//原子加操作,相当于执行refs->mStrong +(-0x1000000),最终mStrong=1refs->mBase->onFirstRef(); //如果是第一次引用,则调用onFirstRef,这个函数很重要,派生类可以重载这个函数,完成一些初始化工作。
}
这里继续分析refs->incWeak的方法,如下所示:
void RefBase::weakref_type::incWeak(const void* id)
{weakref_impl* const impl = static_cast<weakref_impl*>(this);impl->addWeakRef(id);//这句不考虑,Passconst int32_t c __unused = android_atomic_inc(&impl->mWeak);//原子操作,影子对象的弱引用计数加1ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
sp构造初始化完成后RefBase中影子对象的强引用计数变为1,弱引用计数也变为1。
特殊说明:
- 影子对象的强弱引用计数的值,这是彻底理解sp和wp的关键。
- android_atomic_xxx是Android平台提供的原子操作函数,是多线程编程中的常见函数。
- 因为这是release版走的分支。debug的代码是给创造RefBase、 sp,以及wp的人调试用的,因此分析流程时不考虑debug相关代码。以下几个函数不用考虑,仅为调试人员使用。
碰到它们,可以直接跳过,不影响流程的分析:
void addWeakRef(const void* /*id*/) { }
void addStrongRef(const void* /*id*/) { }
void removeStrongRef(const void* /*id*/) { }
void addWeakRef(const void* /*id*/) { }
void removeWeakRef(const void* /*id*/) { }
void printRefs() const { }
void trackMe(bool, bool) { }
@3 程序继续运行,现在到了
wp<A> wpA(spA)
wp有好几个构造函数,这里仅列出最关键的,如下所示:
template<typename T>wp<T>::wp(T* other): m_ptr(other)//wp的成员变量m_ptr指向实际对象
{if (other) m_refs = other->createWeak(this);//调用pA的createWeak,并且保存返回值到成员变量m_refs中
}
...//其他构造器略过
继续分析creatWeak,代码如下:
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{mRefs->incWeak(id);//影子对象的弱引用计数增加1return mRefs; //返回影子对象
}
wp构造初始化完成后影子对象的弱引用计数将增加1。
@4 构造初始化结束总结:
- 现在弱引用计数为2,而强引用计数仍为1。
- wp中有两个成员变量,一个保存实际对象,另一个保存影子对象。
- sp只有一个成员变量用来保存实际对象,但这个实际对象内部已包含了对应的影子对象
- 正常使用该sp化和wp化是引用计数不变的,当销毁对象时,进入到析构方法时才会发生变化。
接下来开始进行析构的流程
@5 wp的析构,析构方法如下所示:
template<typename T>
wp<T>::~wp()
{if (m_ptr) m_refs->decWeak(this);
}
继续分析decWeak,代码如下:
void RefBase::weakref_type::decWeak(const void* id)
{//把基类指针转换成子类(影子对象)的类型,这种做法有些违背面向对象编程的思想weakref_impl* const impl = static_cast<weakref_impl*>(this);impl->removeWeakRef(id);//这句不考虑,Passconst int32_t c = android_atomic_dec(&impl->mWeak); //原子减1,返回旧值,c=2,而弱引用计数从2变为1ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);if (c != 1) return;//c=2,直接返回//如果c为1,则弱引用计数为0,这说明没有弱引用指向实际对象,需要考虑是否释放内存// OBJECT_LIFETIME_XXX和生命周期有关系,1.3中会对此做详细解释//这里OBJECT_LIFETIME_WEAK=0,OBJECT_LIFETIME_STRONG=1if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {if (impl->mStrong == INITIAL_STRONG_VALUE) {delete impl->mBase;} else {delete impl;}} else {impl->mBase->onLastWeakRef(id);if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {delete impl->mBase;}}
}
这里因为之前sp和wp分别执行构造器,所以,弱引用计数为2。wp析构后,弱引用计数减1。但由于此时强引用计数和弱引用计数仍为1,所以没有对象被干掉,即没有释放实际对象和影子对象占据的内存。
@6 sp的析构,析构方法如下所示:
template<typename T>
sp<T>::~sp()
{if (m_ptr) m_ptr->decStrong(this);//调用实际对象的decStrong。由RefBase实现
}
继续分析decStrong,代码如下:
void RefBase::decStrong(const void* id) const
{weakref_impl* const refs = mRefs;refs->removeStrongRef(id);//这句不考虑,Passconst int32_t c = android_atomic_dec(&refs->mStrong);//注意,此时强弱引用计数都是1,下面函数调用的结果是c=1,强引用计数为0ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);if (c == 1) {//对于我们的例子, c为1refs->mBase->onLastStrongRef(id);//调用onLastStrongRef,表明强引用计数减为0,对象有可能被delete//mFlags为0,所以会通过delete this把自己干掉。注意,此时弱引用计数仍为1// OBJECT_LIFETIME_XXX和生命周期有关系,1.3中会对此做详细解释if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {delete this;}}//注意,实际数据对象已经被干掉了,所以mRefs也没有用了,但是decStrong刚进来时就保存mRefs到refs了,所以这里的refs指向影子对象refs->decWeak(id);//调用影子对象decWeak
}
先看delete this的处理,它会导致A的析构函数被调用,A的析构函数,代码如下所示:
//A是继承RefBase的,因此,A的析构直接导致进入RefBase的析构。
RefBase::~RefBase()
{if (mRefs->mStrong == INITIAL_STRONG_VALUE) {delete mRefs;} else {if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {if (mRefs->mWeak == 0) { //这里弱引用计数不为0,而是1delete mRefs;}}}const_cast<weakref_impl*&>(mRefs) = NULL;
}
RefBase的delete this自杀行为没有把影子对象销毁,但还在decStrong中,可接着从delete this往下看,到影子对象的decWeak方法,代码如下:
void RefBase::weakref_type::decWeak(const void* id)
{weakref_impl* const impl = static_cast<weakref_impl*>(this);impl->removeWeakRef(id);//调用前影子对象的弱引用计数为1,强引用计数为0,调用结束后c=1,弱引用计数为0const int32_t c = android_atomic_dec(&impl->mWeak);ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this); if (c != 1) return;// OBJECT_LIFETIME_XXX和生命周期有关系,1.3中会对此做详细解释//这次弱引用计数终于变为0,并且mFlags为0, mStrong也为0。if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {if (impl->mStrong == INITIAL_STRONG_VALUE) {delete impl->mBase;} else {delete impl;//impl就是this,把影子对象自己干掉}} else {impl->mBase->onLastWeakRef(id);if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {delete impl->mBase;}}
}
@7 总结
- RefBase中有一个隐含的影子对象weakRefImpl,该影子对象内部有强弱引用计数。
- sp化后,强弱引用计数各增加1,sp析构后,强弱引用计数各减1。
- wp化后,弱引用计数增加1,wp析构后,弱引用计数减1。
完全彻底地消灭RefBase对象,包括让实际对象和影子对象灭亡,这些都是由强弱引用计数控制的,另外还要考虑flag的取值情况。当flag为0时,可得出如下结论:
- 强引用为0将导致实际对象被delete。
- 弱引用为0将导致影子对象被delete。
2 弱引用变成强引用
这里以使用实例进行说明与分析:
int main()
{A *pA =new A();wp<A> wpA(A);sp<A> spA = wpA.promote();//通过promote函数,得到一个sp
}
wp化后弱引用计数加1,所以此处wp化的结果是:影子对象的弱引用计数为1,强引用计数仍然是初始值0x1000000,wpA的promote函数是从一个弱对象产生一个强对象的重要函数,试看:
@1 由弱引用变成强引用的方法解析
template<typename T>sp<T> wp<T>::promote() const
{sp<T> result;if (m_ptr && m_refs->attemptIncStrong(&result)) {result.set_pointer(m_ptr);//调用sp的构造函数}return result;
}
这里分析set_pointer,代码如下:
template<typename T>
void sp<T>::set_pointer(T* ptr) {m_ptr = ptr;
}
@2 由弱引用变成强引用的关键函数是attemptIncStrong,它的代码如下所示:
bool RefBase::weakref_type::attemptIncStrong(const void* id)
{incWeak(id);//增加弱引用计数,此时弱引用计数变为2weakref_impl* const impl = static_cast<weakref_impl*>(this);int32_t curCount = impl->mStrong;//这个仍是初始值ALOG_ASSERT(curCount >= 0,"attemptIncStrong called on %p after underflow", this);//该循环在多线程操作同一个对象时可能会循环多次。这里可以不去管它,目的就是使强引用计数增加1while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {break;}curCount = impl->mStrong;}// OBJECT_LIFETIME_XXX和生命周期有关系,1.3中会对此做详细解释if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {if (curCount <= 0) {decWeak(id); //弱引用-1return false;}while (curCount > 0) {if (android_atomic_cmpxchg(curCount, curCount + 1,&impl->mStrong) == 0) {break;}curCount = impl->mStrong;}if (curCount <= 0) {decWeak(id); //弱引用-1并返回false,表示强引用计数增加失败return false;}} else {if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {decWeak(id); //弱引用-1并返回false,表示强引用计数增加失败return false;}curCount = android_atomic_inc(&impl->mStrong);}if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {impl->mBase->onLastStrongRef(id);//交给派生类处理}}impl->addStrongRef(id);//这句不考虑,PasscurCount = 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;}curCount = impl->mStrong;}return true;
}
这里对android_atomic_cmpxchg进行说明:android_atomic_cmpxchg(oldValue, newValue,&impl->mstrong);
逻辑解析:如果impl->mstrong == oldValue,则impl->mstrong == newValue,返回1;否则返回0;
总结:promote完成后,相当于增加了一个强引用。由弱生强成功后,强弱引用计数均增加1。当前影子对象的强引用计数为1,弱引用计数为2。
同时这里对RefBase的extendObjectLifetime方法解读(解释OBJECT_LIFETIME_WEAK和OBJECT_LIFETIME_STRONG),RefBase中定义了一个关键枚举和一个方法extendObjectLifetime:
//! Flags for extendObjectLifetime()enum {OBJECT_LIFETIME_STRONG = 0x0000,OBJECT_LIFETIME_WEAK = 0x0001,OBJECT_LIFETIME_MASK = 0x0001};
观察flags为OBJECT_LIFETIME_WEAK的情况,见实例代码:
class A:public RefBase
{publicA(){extendObjectLifetime(OBJECT_LIFETIME_WEAK);//在构造函数中调用}
}int main()
{A *pA =new A();wp<A> wpA(A);//弱引用计数加1{sp<A>spA(pA) //sp后,结果是强引用计数为1,弱引用计数为2}
....
}
@@2.1 sp的析构将直接调用RefBase的decStrong,它的代码如下所示:
void RefBase::decStrong(const void* id) const
{weakref_impl* const refs = mRefs;refs->removeStrongRef(id);//这句不考虑,Passconst int32_t c = android_atomic_dec(&refs->mStrong);//注意,此时强引用计数都是1,弱引用计数是2,下面函数调用的结果是c=1,强引用计数为0ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);if (c == 1) {//对于我们的例子, c为1refs->mBase->onLastStrongRef(id);//调用onLastStrongRef,表明强引用计数减为0,对象有可能被delete//mFlags为0,所以会通过delete this把自己干掉。注意,此时弱引用计数仍为1//注意这句话。如果flags不是WEAK,将直接delete数据对象,现在我们的flags是WEAK,所以不会delete它if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {delete this;}}refs->decWeak(id);//调用影子对象decWeak,调用前引用计数为2
}
@@2.2 调用影子对象的decWeak。再来看它的处理,代码如下所示:
void RefBase::weakref_type::decWeak(const void* id)
{weakref_impl* const impl = static_cast<weakref_impl*>(this);impl->removeWeakRef(id);//调用前影子对象的弱引用计数为1,强引用计数为0,调用结束后c=1,弱引用计数为0const int32_t c = android_atomic_dec(&impl->mWeak);ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);if (c != 1) return; //c为2,弱引用计数为1,直接返回。//若到了wp析构之处,也会调用decWeak,调用上边的原子减操作后c=1,弱引用计数变为0,此时会继续往下运行。//由于mFlags为WEAK ,所以不满足if的条件if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {if (impl->mStrong == INITIAL_STRONG_VALUE) {delete impl->mBase;} else {delete impl;//impl就是this,把影子对象自己干掉}} else {//会走这里impl->mBase->onLastWeakRef(id);//由于flags值满足下面这个条件,所以实际对象会被delete,实际对象的delete会检查影子对象的弱引用计数,若它为0,则会把影子对象也delete掉。//由于影子对象的弱引用计数此时已经为0,所以影子对象也会被delete。if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {delete impl->mBase;}}
}
@3 总结
@@3.1 flags为LIFETIME_STRONG(即0),强引用计数控制实际对象的生命周期,弱引用计数控制影子对象的生命周期。强引用计数为0后,实际对象被delete。所以对于这种情况,应记住的是,使用wp时要由弱生强,以免收到segment fault信号。
@@3.2 flags为LIFETIME_WEAK(即1)强引用计数为0,弱引用计数不为0时,实际对象不会被delete。当弱引用计数减为0时,实际对象和影子对象会同时被delete。
3 轻量级的引用计数控制LightRefBase
RefBase是一个重量级的引用计数控制类。Android为我们提供了一个轻量级的RefBase,即LightRefBase。
@1 LightRefBase的实现,查看其代码:
template <class T>
class LightRefBase
{
public:inline LightRefBase() : mCount(0) { }inline void incStrong(__attribute__((unused)) const void* id) const {//LightRefBase只有一个引用计数控制量mCount。incStrong的时候使它增加1android_atomic_inc(&mCount);}inline void decStrong(__attribute__((unused)) const void* id) const {//decStrong的时候减1,当引用计数变为零的时候,delete掉自己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;//引用计数控制变量
};
@2 使用实例(LightRefBase是一个模板类,其中类A是从LightRefBase派生,写法如下:
class A:public LightRefBase<A> //注意派生的时候要指明是LightRefBase<A>
{
public:
A(){};
~A(){};
};
从LightRefBase的定义中可以知道,它支持sp控制,因为它只有incStrong和decStrong。
android系统核心机制 基础(01)智能指针wp sp相关推荐
- 【C++】Android (Light)RefBase-sp-wp引用计数-智能指针源码分析
文章目录 1.RefBase简介 2.RefBase源码分析 3.RefBase使用注意事项 4.总结 1.RefBase简介 什么是RefBase?RefBase是Android中的一个C++类,用 ...
- VTK修炼之道80:VTK开发基础_智能指针与引用计数
1.引用计数 VTK经过多年的开发与维护,已经形成了一套稳定的框架和开发规则.因此,了解这些规则和框架是定制VTK类的基础,这其中用到了大量面向对象的设计模式,例如对象工程模式.观察者/命令模式:还有 ...
- android ndk r8 mac,c – 智能指针不适用于Android NDK r8
我不知道如何在我的 Android项目中使用共享指针.我在Mac OS X上使用最新的Eclipse ADT与Android NDK r8d. 这是我的Android.mk文件中的内容: LOCAL_ ...
- Android系统核心机制之APP启动的程序入口ActivityThread的简单介绍
Android中为什么主线程不会因为Looper.loop()里的死循环阻塞? 标题是伪命题 参考资料 Android中为什么主线程不会因为Looper.loop()里的死循环卡死? 知乎 之前对这个 ...
- C++入门基础01:指针与引用
C++入门基础:指针与引用 指针 #include <iostream> //系统定义头文件一般是尖括号 #include<fstream> #include<strin ...
- Android 智能指针 视频,Android系统智能指针中轻量级指针
lp.sp.wp在Android Native层中被大量使用,所以非常有必要学习它们的实现原理.lp是Light Pointer的缩写,表示轻量级指针,sp是Strong Pointer的缩写,表示强 ...
- Android智能指针——读书笔记
目录结构 目录结构 参考资料 概述 背景知识 GC经典问题 轻量级指针 实现原理分析 构造函数 析构函数 应用实例分析 强指针和弱指针 强指针的实现原理分析 增加对象的弱引用计数 增加对象的强引用计数 ...
- Android系统原理性问题分析 - RefBase、sp、wp 分析
声明 在Android系统中经常会遇到一些系统原理性的问题,在此专栏中集中来讨论下. 接触Android系统,遇到很多sp.wp相关问题,此篇分析Android系统内的智能指针问题. 此篇参考一些博客 ...
- C++智能指针详解【C++智能指针】
自动内存管理 智能指针 什么是 RAII 原理 智能指针的模板(template)实现 auto_ptr auto_ptr 使用 重载函数 operator-> / *语法格式 自实现 auto ...
最新文章
- KMM 搭建环境,并运行安卓和ios
- Windbg/KD驱动调试点滴–将平时调试的一些小方法共享给大家 --------- 转
- 华为ipd产品开发流程_华为集成产品开发(IPD)流程的解读
- python tkinter计算器实例_python -Tkinter 实现一个小计算器功能
- MongoDB在Linux下常用优化设置
- 光伏项目用地政策解析
- 【计算机图形学】画线算法——中点画线算法
- 网页版模仿Excel
- 一个简单的姓名生成器
- 软工网络15团队作业4——Alpha阶段敏捷冲刺之Scrum 冲刺博客(Day6)
- 面试技巧---白话文
- 微信公众号运营助手,可以在手机上回复粉丝留言
- 一部分使用CNES后处理BIA产品的PPP-AR结果
- 用java实现从txt文本文件批量导入数据至数据库
- Android5.0以上系统的移动网络开关
- Linux:系统进程---->查看命令【ps:静态查看进程】【top:动态查看进程】
- 软件测试-制定测试策略
- OpenAI-ChatGPT最新官方接口《审核机制》全网最详细中英文实用指南和教程,助你零基础快速轻松掌握全新技术(七)(附源码)
- 设计模式(一) 工厂模式 五种写法总结
- 软件工程——软件设计总结
热门文章
- python怎么批量下载年报_如何用Python写一个抓取新浪财经网指定企业年报的脚本...
- QQ思维导图--消息模块
- 【C++】购物街中的商品价格竞猜
- CODESOFT软件报错 無法開啓文件
- ToB创业,要借什么势?
- shell 编程 declare 命令
- Java顺丰同城接口开发
- 学生成绩管理系统mysql课程设计_数据库课程设计(极其简单的学生成绩管理系统)...
- java 3gp 转mp3_java实现音频转换
- C++沉思录上提到的一道练习题及其源码实现