对象的维护
在编程中,维护对象的生命周期的方式有两种方式:
- 可达性算法
- 引用计数算法
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 - 卷一》