Android中的智能指针
对象的维护
在编程中,维护对象的生命周期的方式有两种方式:
- 可达性算法
- 引用计数算法
Java通过可达性管理对象的生命周期GC可达性实践-内存泄露分析。而C++中由于没有GC的机制,通常使用的是引用计数算法。
简单的引用计数通常是给对象添加一个引用计数器,当有引用时,计数器加1,当引用失效时,引用计数减少1,当计数器为0时,说明可以回收对象了。但是此种方式无法解决循环引用的情况。
如图:
A对象引用了B对象,B对象引用了A对象。由于A,B的对象的计数器均不为0,因此永远不会回收A,B对象。
为了解决这个问题,引入了强引用计数和弱引用计数。存在强引用,对象不会回收,但只存在弱引用,对象是可以回收的。对于上面的循环引入强,弱引用,就是下面的情况。
A强引用B,B弱引用A,由于A只有弱引用,A是可以回收的,一旦A回收,无对象强引用B,B也可以回收了,通过强,弱引用就解决了引用计数算法中的循环引用的问题。
Android中的智能指针
Android为C++提供了三种智能指针
- 轻量级指针(sp,LightRefBase),使用简单的引用计数,也就是只有强引用计数。
- 强引用指针(sp,RefBase),使用强引用计数
- 弱引用指针(wp,RefBase),使用弱引用计数
轻量级指针 - LightRefBase
需要使用轻量级指针的类继承LightRefBase
就可以了。LightRefBase
是一个模板类,仅仅实现了简单的引用计数逻辑。
-
mCount
计数器 -
incStrong
添加强引用计数的方法 -
decStrong
减少强应用计数的方法,并且在计数器为0时,删除对象。
class LightClass : public LightRefBase<LightClass> {
};
LightClass
简单继承LightRefBase
。
然后通过sp
来完成引用计数。
int main(int argc, char** argv)
{
LightClass* pLightClass = new LightClass();
sp<LightClass> lpOut = pLightClass;
printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());
{
sp<LightClass> lpInner = lpOut;
printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());
}
printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());
return 0;
}
sp类比较简单,强引用计数的操作是在构造方法和析构方法中。
sp<T>::sp(T* other)
: m_ptr(other) {
if (other)
other->incStrong(this);
}
构造方法中初始化m_ptr
为传递的对象,并调用传递的对象的incStrong
方法来添加强引用计数。也是我们传递的LightRefBase
的子类。
sp<T>::~sp() {
if (m_ptr)
m_ptr->decStrong(this);
}
析构方法中调用incStrong
来较少强引用计数。
基本交互图:
强引用,弱引用原理
强弱引用的原理是三个重要的类
-
RefBase
: 实现了强引用,弱引用计数 -
wp
: 弱引用指针 -
sp
:强引用指针
RefBase
实现了强引用,弱引用计数,要分析强引用,弱引用的原理,需要先分析一下RefBase
。
RefBase
RefBase
实现了强弱引用计数,计数功能通过委托给weakref_imp
来实现的,而weakref_imp
继承自weakref_type
。
Refbase
重要的方法:
-
incStrong
添加强引用计数 -
decStrong
减少强引用计数 -
createWeak
添加弱引用计数 -
extendObjectLifetime
扩展对象回收的时机
weakref_imp
-
mStrong
强引用计数 -
mWeak
弱引用计数 -
mBase
计数的实际对象 -
incWeak
添加弱引用计数 -
decWeak
减少弱引用计数 -
mFlags
回收对象的标识。可以有两个值-
OBJECT_LIFETIME_STRONG
,说明对象只受强引用控制,一旦强引用计数为0,就可以回收对象 -
OBJECT_LIFETIME_WEAK
,说明对象受强引用和弱引用控制,只有在强引用和弱引用均为0时,才可以回收对象。
-
weakref_type
-
incWeak
添加弱引用计数 -
decWeak
减少弱引用计数 -
attemptIncStrong
尝试添加一个强引用 -
attemptIncWeak
尝试去添加一个弱引用
与LightRefBase
使用强弱引用计数一直,想使用RefBase
的强,弱引用功能直接实现此类就可以了。
class LightClass : public RefBase {
};
强引用原理
我们简单的在main
函数中调用类
int main(int argc, char** argv)
{
LightClass* pLightClass = new LightClass();
sp<LightClass> lpOut = pLightClass;
printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());
{
sp<LightClass> lpInner = lpOut;
printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());
}
printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());
return 0;
}
OBJECT_LIFETIME_STRONG
(对象只受强引用控制)标识下的基本的时序图:
我们分析lpOut
是如何使用创建与销毁的。lpInner
原理也是类似。
创建一个LightClass
类,此类实现了RefBase
,接着创建强引用的sp
。
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other) {
if (other)
other->incStrong(this);
}
T* m_ptr;
使用LightClass
类初始化m_ptr
。并调用LightClass
的incStrong
方法,由于LightClass
继承自RefBase
,直接调用RefBase
的incStrong
。
我们一步一来分析。
void RefBase::incStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->incWeak(id);
......
}
第一步是获取weakref_impl
指针,然后调用weakref_impl
的incWeak
方法。weakref_impl
继承自weakref_type
。
我们来分析incWeak
方法。
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 = impl->mWeak.fetch_add(1,
std::memory_order_relaxed);
}
weakref_type
强转为weakref_impl
,然后调用addWeakRef
。
addWeakRef
是什么鬼呢?
在weakref_impl
中,非调试模式下addWeakRef
为空的方法体。此方法是用来调试的,我们不关心。因此遇到下面的方法直接忽略掉。
class RefBase::weakref_impl : public RefBase::weakref_type
{
#if !DEBUG_REFS
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
接着是调用weakref_impl
对象的mWeak
的原子变量来让弱引用计数加1。
然后我们继续分析RefBase::incStrong
的方法。
void RefBase::incStrong(const void* id) const
{
·······
const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
if (c != INITIAL_STRONG_VALUE) {
return;
}
int32_t old = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
std::memory_order_relaxed);
refs->mBase->onFirstRef();
}
接着调用weakref_impl
的mStrong
原子变量来让强引用计数加1。并返回添加计数前的值。mStrong
初始化的值为INITIAL_STRONG_VALUE
,第一次添加计数,因此c
变量为INITIAL_STRONG_VALUE
。接着调用mStrong
的原子变量来减少INITIAL_STRONG_VALUE
计数。此时mStrong
变量的计数就为1了。
然后调用RefBase
的onFirstRef
来表示第一次引用。可以在此方法中做相关的初始化操作。
RefBase
的incStrong
方法来添加强引用计数的流程:
- 调用
weakref_impl
的incWeak
来添加弱引用计数。 - 调用
weakref_impl
的mStrong
的来添加强引用计数。 - 如果是第一次强引用会调用
mStrong
来减少INITIAL_STRONG_VALUE
计数,保证计数为1。 - 如果是第一次强引用调用
onFirstRef
方法。
当main
执行完成之前会回收lpOut
对象,将调用sp
的析构方法。
template<typename T>
sp<T>::~sp() {
if (m_ptr)
m_ptr->decStrong(this);
}
调用m_ptr
的decStrong
,m_ptr
是LightClass
类的对象,LightClass
继承RefBase
。因此会调用到RefBase
的decStrong
的方法。
void RefBase::decStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);
if (c == 1) {
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;
}
}
refs->decWeak(id);
}
获取weakref_impl
对象,由于main
方法中先析构lpInner
对象,在析构lpOut
。因此呢当调用weakref_impl
对象的mStrong
来减少计数的时候,返回的值为1
。if
为true
,接着会调用LightClass
的onLastStrongRef
方法。也就是RefBase
的onLastStrongRef
,表示是最后一次强引用,可以在此方法中做相关的清理工作。然后获取到weakref_impl
标识,我们没有设置因此是OBJECT_LIFETIME_STRONG
,说明对象只受强引用计数影响。if
为true
,delete
当前对象。
然后呢调用weakref_impl
对象的decWeak
来减少弱引用计数。
void RefBase::weakref_type::decWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
//减少弱引用计数
const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release);
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) {
if (impl->mStrong.load(std::memory_order_relaxed)
== INITIAL_STRONG_VALUE) {
} else {
delete impl;
}
} else {
impl->mBase->onLastWeakRef(id);
delete impl->mBase;
}
}
获取weakref_impl
对象并调用mWeak
的fetch_sub
减少弱引用计数。因为现在只有强引用计数,而且是最后一次减少计数。因此会c
变量值为1
。接着获取weakref_impl
的标识,flags
的标识为OBJECT_LIFETIME_STRONG
因此if
为true
。获取weakref_impl
的强引用计数,现在为0
,因此会调用delete
来删除weakref_impl
对象。
还有一个点是在RefBase::decStrong
中调用了delete
来删除当前对象,因此会调用到RefBase
类的析构函数。
RefBase::~RefBase()
{
int32_t flags = mRefs->mFlags.load(std::memory_order_relaxed);
if ((flags & OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) {
delete mRefs;
}
} else if (mRefs->mStrong.load(std::memory_order_relaxed)
== INITIAL_STRONG_VALUE) {
delete mRefs;
}
const_cast<weakref_impl*&>(mRefs) = NULL;
}
获取weakref_impl
的标识,因为我们使用的的是默认的OBJECT_LIFETIME_STRONG
,因此if不满足,接着是获取weakref_impl
的强引用计数,返回的为0,else if也不满足。接着是设置当前的weakref_impl
的指针为NULL
RefBase
减少弱引用计数的的基本步骤为:
- 减少强引用对象
- 如果是
Flag
是OBJECT_LIFETIME_STRONG
,并且强引用计数为0,就删除当前的RefBase
, - 调用
weakref_impl
对象的decWeak
的方法来减少弱引用计数。
弱引用原理
wp
关键的字段与方法
-
m_ptr
指向引用对象的指针 -
m_refs
指向引用对象的weakref_type
对象 -
promote
用于转化当前的弱引用对象为强引用。
int main(int argc, char** argv)
{
LightClass* pLightClass = new LightClass();
sp<LightClass> lpOut = pLightClass;
printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());
{
wp<LightClass> wpInner = pLightClass;
printf("Light Ref weak Count: %d.\n", pLightClass->getWeakRefs()->getWeakCount());
}
printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());
return 0;
}
首先创建wp对象。
wp<T>::wp(T* other)
: m_ptr(other)
{
if (other) m_refs = other->createWeak(this);
}
使用传递的pLightClass
的初始化m_ptr
,并调用pLightClass
的createWeak
的方法添加弱引用,并返回weakref_type
对象指针来初始化m_refs
对象。
接着我们来分析RefBase
的createWeak
的方法。
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
mRefs->incWeak(id);
return mRefs;
}
调用当前的weakref_impl
对象的incWeak方法。并返回当前的weakref_impl
对象。
接着分析weakref_impl
的incWeak
。
void RefBase::weakref_type::incWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
const int32_t c __unused = impl->mWeak.fetch_add(1,
std::memory_order_relaxed);
}
只是简单的获取mWeak
变量来添加弱引用计数。
对于~wp
也很简单。
template<typename T>
wp<T>::~wp()
{
if (m_ptr) m_refs->decWeak(this);
}
调用weak_type
来的decWeak
方法来减少弱引用。此方法在强引用一节已经说明。
对于wp
还有一个特别重要的方法 - promote
此方法用于尝试把当前的弱引用转化成强引用。
sp<T> wp<T>::promote() const
{
sp<T> result;
if (m_ptr && m_refs->attemptIncStrong(&result)) {
result.set_pointer(m_ptr);
}
return result;
}
此方法很简单,就是调用weak_type
的attemptIncStrong
来看能不能添加强引用。
我们接下来分析weakref_type
的attemptIncStrong
方法。一步一步分析:
bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
incWeak(id);
weakref_impl* const impl = static_cast<weakref_impl*>(this);
int32_t curCount = impl->mStrong.load(std::memory_order_relaxed);
······
}
显示调用incWeak
方法来添加弱引用计数。前面已经分析过了,这里不再说明。
然后获取mStrong
强引用计数的当前值。
bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
······
while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
if (impl->mStrong.compare_exchange_weak(curCount, curCount+1,
std::memory_order_relaxed)) {
break;
}
}
······
}
获取的当前的强引用计数curCount
大于0,并且不是初始值,将调用mStrong的compare_exchange_weak
来让mStrong的值加1。
bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
······
if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
if (curCount <= 0) {
decWeak(id);
return false;
}
while (curCount > 0) {
if (impl->mStrong.compare_exchange_weak(curCount, curCount+1,
std::memory_order_relaxed)) {
break;
}
}
if (curCount <= 0) {
decWeak(id);
return false;
}
}
······
}
······
}
如果当前的强引用计数小于等于0或者是初始化的值,先获取weakref_imp
的标识,如果是强引用执行if语句。如果当前的强引用计数curCount为小于等于0,说明无强引用引用RefBase对象。说明对象已经回收。此时直接减少弱引用的值,并返回fasle。若是curCount
的值大于0,执行while
语句。尝试让mStrong
加1。接下来还是判断curCount
是否小于等于0,如果是就直接减少弱引用,返回false
。
bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
······
if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
······
else {
if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {
decWeak(id);
return false;
}
curCount = impl->mStrong.fetch_add(1, std::memory_order_relaxed);
if (curCount != 0 && curCount != INITIAL_STRONG_VALUE) {
impl->mBase->onLastStrongRef(id);
}
}
}
······
}
如果当前的weakref_imp
的标志是OBJECT_LIFETIME_WEAK
。将执行else,先调用对象的onIncStrongAttempted
的方法判断支不支持增加强引用,如果不支持就直接的减少弱引用,返回false。如果支持就直接强引用计数加1。
bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
······
if (curCount == INITIAL_STRONG_VALUE) {
impl->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
std::memory_order_relaxed);
}
return true;
}
如果强引用计数的先前值curCount
为INITIAL_STRONG_VALUE
就减去出事的值。最终返回false。
参考资料
- 《深入理解Java虚拟机》
- 《Android系统源代码情景分析》
- 《深入理解Android - 卷一》