Android中的智能指针

对象的维护

在编程中,维护对象的生命周期的方式有两种方式:

  1. 可达性算法
  2. 引用计数算法

Java通过可达性管理对象的生命周期GC可达性实践-内存泄露分析。而C++中由于没有GC的机制,通常使用的是引用计数算法。

简单的引用计数通常是给对象添加一个引用计数器,当有引用时,计数器加1,当引用失效时,引用计数减少1,当计数器为0时,说明可以回收对象了。但是此种方式无法解决循环引用的情况。

如图:
Android中的智能指针

A对象引用了B对象,B对象引用了A对象。由于A,B的对象的计数器均不为0,因此永远不会回收A,B对象。

为了解决这个问题,引入了强引用计数和弱引用计数。存在强引用,对象不会回收,但只存在弱引用,对象是可以回收的。对于上面的循环引入强,弱引用,就是下面的情况。

Android中的智能指针

A强引用B,B弱引用A,由于A只有弱引用,A是可以回收的,一旦A回收,无对象强引用B,B也可以回收了,通过强,弱引用就解决了引用计数算法中的循环引用的问题。

Android中的智能指针

Android为C++提供了三种智能指针

  • 轻量级指针(sp,LightRefBase),使用简单的引用计数,也就是只有强引用计数。
  • 强引用指针(sp,RefBase),使用强引用计数
  • 弱引用指针(wp,RefBase),使用弱引用计数

轻量级指针 - LightRefBase

Android中的智能指针

需要使用轻量级指针的类继承LightRefBase就可以了。LightRefBase是一个模板类,仅仅实现了简单的引用计数逻辑。

  • mCount计数器
  • incStrong添加强引用计数的方法
  • decStrong减少强应用计数的方法,并且在计数器为0时,删除对象。
class LightClass : public LightRefBase<LightClass> {
};

LightClass简单继承LightRefBase

然后通过sp来完成引用计数。

Android中的智能指针

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来较少强引用计数。

基本交互图:

Android中的智能指针

强引用,弱引用原理

强弱引用的原理是三个重要的类

  • RefBase : 实现了强引用,弱引用计数
  • wp : 弱引用指针
  • sp:强引用指针

RefBase实现了强引用,弱引用计数,要分析强引用,弱引用的原理,需要先分析一下RefBase

RefBase

RefBase实现了强弱引用计数,计数功能通过委托给weakref_imp来实现的,而weakref_imp继承自weakref_type

Android中的智能指针

Refbase 重要的方法:

  • incStrong添加强引用计数
  • decStrong减少强引用计数
  • createWeak添加弱引用计数
  • extendObjectLifetime扩展对象回收的时机

weakref_imp

  • mStrong强引用计数
  • mWeak弱引用计数
  • mBase计数的实际对象
  • incWeak添加弱引用计数
  • decWeak 减少弱引用计数
  • mFlags回收对象的标识。可以有两个值
    1. OBJECT_LIFETIME_STRONG,说明对象只受强引用控制,一旦强引用计数为0,就可以回收对象
    2. 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(对象只受强引用控制)标识下的基本的时序图:

Android中的智能指针

我们分析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。并调用LightClassincStrong方法,由于LightClass继承自RefBase,直接调用RefBaseincStrong

我们一步一来分析。

void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->incWeak(id);
    ......
}

第一步是获取weakref_impl指针,然后调用weakref_implincWeak方法。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_implmStrong原子变量来让强引用计数加1。并返回添加计数前的值。mStrong初始化的值为INITIAL_STRONG_VALUE,第一次添加计数,因此c变量为INITIAL_STRONG_VALUE。接着调用mStrong的原子变量来减少INITIAL_STRONG_VALUE计数。此时mStrong变量的计数就为1了。

然后调用RefBaseonFirstRef来表示第一次引用。可以在此方法中做相关的初始化操作。

RefBaseincStrong方法来添加强引用计数的流程:

  • 调用weakref_implincWeak来添加弱引用计数。
  • 调用weakref_implmStrong的来添加强引用计数。
  • 如果是第一次强引用会调用mStrong来减少INITIAL_STRONG_VALUE计数,保证计数为1。
  • 如果是第一次强引用调用onFirstRef方法。

main执行完成之前会回收lpOut对象,将调用sp的析构方法。

template<typename T>
sp<T>::~sp() {
    if (m_ptr)
        m_ptr->decStrong(this);
}

调用m_ptrdecStrongm_ptrLightClass类的对象,LightClass继承RefBase。因此会调用到RefBasedecStrong的方法。

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来减少计数的时候,返回的值为1iftrue,接着会调用LightClassonLastStrongRef方法。也就是RefBaseonLastStrongRef,表示是最后一次强引用,可以在此方法中做相关的清理工作。然后获取到weakref_impl标识,我们没有设置因此是OBJECT_LIFETIME_STRONG,说明对象只受强引用计数影响。iftruedelete当前对象。

然后呢调用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对象并调用mWeakfetch_sub减少弱引用计数。因为现在只有强引用计数,而且是最后一次减少计数。因此会c变量值为1。接着获取weakref_impl的标识,flags的标识为OBJECT_LIFETIME_STRONG因此iftrue。获取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减少弱引用计数的的基本步骤为:

  • 减少强引用对象
  • 如果是FlagOBJECT_LIFETIME_STRONG,并且强引用计数为0,就删除当前的RefBase
  • 调用weakref_impl对象的decWeak的方法来减少弱引用计数。

弱引用原理

Android中的智能指针

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

Android中的智能指针

首先创建wp对象。

wp<T>::wp(T* other)
    : m_ptr(other)
{
    if (other) m_refs = other->createWeak(this);
}

使用传递的pLightClass的初始化m_ptr,并调用pLightClasscreateWeak的方法添加弱引用,并返回weakref_type对象指针来初始化m_refs对象。

接着我们来分析RefBasecreateWeak的方法。

RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
    mRefs->incWeak(id);
    return mRefs;
}

调用当前的weakref_impl对象的incWeak方法。并返回当前的weakref_impl对象。

接着分析weakref_implincWeak

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_typeattemptIncStrong来看能不能添加强引用。

我们接下来分析weakref_typeattemptIncStrong方法。一步一步分析:

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

如果强引用计数的先前值curCountINITIAL_STRONG_VALUE就减去出事的值。最终返回false。

参考资料

  • 《深入理解Java虚拟机》
  • 《Android系统源代码情景分析》
  • 《深入理解Android - 卷一》