Android智能指针

C++ RAII对象

RAII的全称是Resource Acquisition Is Initialization,即“资源获取就是初始化”。RAII的做法是使用一个对象,在其构造时获取对应的资源,在对象生命期内控制对资源的访问,使之始终保持有效,最后在对象析构的时候,释放构造时获取的资源。把资源放到对象里面,便可依赖C++的构造函数和析构函数机制,确保对资源的持有和释放。

智能指针便是属于RAII的一种,智能指针是一个用于管理资源的对象,其使用引用计数技术。在智能指针对象构造时,增加它所引用对象的引用计数,在其析构时,减少它所持有对象的引用计数。当引用对象的引用计数为0时,便释放引用对象。
引用计数存放在引用对象中,在智能指针对象构造时,需要将待引用对象传入到智能指针对象的构造函数中,在构造函数会对引用对象的引用计数加1,当智能指针对象析构时,就将其所引用对象的引用计数减1。

Android内置的智能指针包含三种:轻量级指针、强指针、弱指针。

轻量级指针

轻量级指针通过简单的引用计数技术来维护对象的生命周期,下文从代码角度分析下轻量级指针的实现原理。

LightRefBase 类

// code path: /system/core/include/utils/lightRefBase.h
template <class T>
class LightRefBase
{
public:
    inline LightRefBase() : mCount(0) { }
    inline void incStrong(__attribute__((unused)) const void* id) const {
        mCount.fetch_add(1, std::memory_order_relaxed);
    }
    inline void decStrong(__attribute__((unused)) const void* id) const {
        if (mCount.fetch_sub(1, std::memory_order_release) == 1) {
            std::atomic_thread_fence(std::memory_order_acquire);
            delete static_cast<const T*>(this);
        }
    }
    //! DEBUGGING ONLY: Get current strong ref count.
    inline int32_t getStrongCount() const {
        return mCount.load(std::memory_order_relaxed);
    }

    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 std::atomic<int32_t> mCount;	// 对象的引用计数
};

任何需要使用轻量级指针的类对象都必须要继承 LightRefBase 类。LightRefBase是一个模板类,其中模板参数T表示对象的实际类型,它必须是继承了 LightRefBase 类的。其成员变量mCount用来描述对象的的引用计数值,提供了两个public接口 incStrongdecStrong 来增加和减少对象的引用计数值。在成员函数decStrong中,当对象的引用计数值为1时,其减少之后就会变成0,就表示需要需要释放这个对象所占用的内存了。

轻量级指针的实现类 sp

我们有了轻量级指针所指向的对象,那么这个对象由谁来指向?即谁来调用对象的 incStrongdecStrong 函数?接下来需要真正的轻量级指针的实现类,Android提供的轻量级指针的实现类是 sp ,它也是强指针的实现类,这里仅关注它与轻量级指针相关的实现。

template<typename T>
class sp {
public:
    inline sp() : m_ptr(nullptr) { }

    sp(T* other);  // NOLINT(implicit)
    sp(const sp<T>& other);
    sp(sp<T>&& other);
    template<typename U> sp(U* other);  // NOLINT(implicit)
    template<typename U> sp(const sp<U>& other);  // NOLINT(implicit)
    template<typename U> sp(sp<U>&& other);  // NOLINT(implicit)

    ~sp();

    // Assignment

    sp& operator = (T* other);
    sp& operator = (const sp<T>& other);
    sp& operator = (sp<T>&& other);

    template<typename U> sp& operator = (const sp<U>& other);
    template<typename U> sp& operator = (sp<U>&& other);
    template<typename U> sp& operator = (U* other);

    //! Special optimization for use by ProcessState (and nobody else).
    void force_set(T* other);

    // Reset

    void clear();

    // Accessors

    inline T&       operator* () const     { return *m_ptr; }
    inline T*       operator-> () const    { return m_ptr;  }
    inline T*       get() const            { return m_ptr; }
    inline explicit operator bool () const { return m_ptr != nullptr; }

    // Operators

    COMPARE(==)
    COMPARE(!=)
    COMPARE(>)
    COMPARE(<)
    COMPARE(<=)
    COMPARE(>=)

private:    
    template<typename Y> friend class sp;
    template<typename Y> friend class wp;
    void set_pointer(T* ptr);
    T* m_ptr;
};

sp 类也是一个模板类,模板参数T表示所引用对象的实际类型,该类型必须继承自 LightRefBase 类。sp 也是强指针的实现类,这里仅关注其对于轻量级指针的实现:

T* m_prt;	//该成员变量用于指向所引用的对象,该对象必须继承自 LightRefBase

template<typename T>
sp<T>::sp(T* other)
        : m_ptr(other) {
    if (other)
        other->incStrong(this);
}

template<typename T>
sp<T>::sp(const sp<T>& other)
        : m_ptr(other.m_ptr) {
    if (m_ptr)
        m_ptr->incStrong(this);
}

template<typename T>
sp<T>& sp<T>::operator =(T* other) {
    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
    if (other) other->incStrong(this);
    if (oldPtr) oldPtr->decStrong(this);
    if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
    m_ptr = other;
    return *this;
}

template<typename T>
sp<T>& sp<T>::operator =(const sp<T>& other) {
    // Force m_ptr to be read twice, to heuristically check for data races.
    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
    T* otherPtr(other.m_ptr);
    if (otherPtr) otherPtr->incStrong(this);
    if (oldPtr) oldPtr->decStrong(this);
    if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
    m_ptr = otherPtr;
    return *this;
}

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

sp 类和轻量级相关的就是它的构造函数、赋值操作符以及、析构函数。构造函数和析构函数的逻辑非常简单,构造时将所引用对象的引用计数加1,析构时将所引用对象的引用计数减1,都是调用 LightRefBase 的接口。
它的赋值操作符的逻辑就会稍有点复杂,再次先回忆下智能指针的原理:当引用对象有智能指针指向它时,就会给它的引用计数加1;当指向了所引用对象的智能指针不在指向改对象时,它的引用计数就会减1。赋值函数的逻辑同理,由于智能指针指向的新的对象,就会增加新对象的引用计数,而减少旧对象的引用计数。

UML类图

轻量级指针类图

强指针和弱指针

轻量级指针属于简单引用计数技术,它存在这样一个缺陷:两个对象互相通过智能指针引用时,最终都无法通过智能指针析构。 如下:

class A: public LightRefBase<A> {
    sp<B> pB;
};
class B: public LightRefBase<B> {
    sp<A> pA:
}

sp<A> p_a = new A();
sp<B> p_b = new B();
p_a->pB = p_b;
p_b->pA = p_a;
p_a = NULL;
p_b = NULL;

因此,需要一种更为复杂的引用计数技术来维护对象的声明周期,即强引用计数和弱引用计数。这种新的引用计数方式,有种用法可以规定对象的生命周期只受强引用计数控制。例:上述对象A强引用B,对象B弱引用A,当p_a和p_b置为NULL后,对象A就可以析构,然后对象B也会被析构。

引用计数类——RefBase

强弱引用计数类的实现实现如下(删除了相关的bug代码,仅保留精华实现代码):

// path: system/core/libutils/include/utils/RefBase.h
class RefBase
{
public:
    void incStrong(const void* id) const;    // 增加强引用计数和弱引用计数
    void decStrong(const void* id) const;   // 减少强应用计数和弱引用计数

    class weakref_type
    {
    public:
        void incWeak(const void* id);    // 实际增加弱引用计数的函数
        void decWeak(const void* id);   // 实际减少弱引用计数的函数

        bool attemptIncStrong(const void* id);  // 尝试增加强引用计数,用于弱指针
        bool attemptIncWeak(const void* id);    // 尝试增加弱引用计数
    };
    weakref_type* createWeak(const void* id) const; // 增加强引用计数,并返回weakref_type*
    weakref_type* getWeakRefs() const;                        // 返回weakref_type*
 
protected:
    RefBase();
    virtual ~RefBase();

    enum {  //! Flags for extendObjectLifetime() 
        OBJECT_LIFETIME_STRONG  = 0x0000,   // 由强引用计数维护对象生命周期(默认值)
        OBJECT_LIFETIME_WEAK      = 0x0001,    // 由弱引用计数维护对象生命周期
        OBJECT_LIFETIME_MASK      = 0x0001
    };
    void extendObjectLifetime(int32_t mode);

    // 当对象的生命周期受弱引用计数控制时,该函数判断是否允许将其升级到强指针
    enum {  //! Flags for onIncStrongAttempted()
        FIRST_INC_STRONG = 0x0001
    };
    virtual bool onIncStrongAttempted(uint32_t flags, const void* id);  // 对象的生命周期

    virtual void onFirstRef();                      // 对象第一次被强指针引用
    virtual void onLastStrongRef(const void* id);   // 最后一个引用对象的强指针不再引用对象
    virtual void onLastWeakRef(const void* id);     // 最后一个引用对象的弱指针不再引用对象

private:
    friend class weakref_type;
    class weakref_impl;

    RefBase(const RefBase& o);
    RefBase& operator=(const RefBase& o);

    weakref_impl* const mRefs;  // 描述引用计数的对象
};

// path: system/core/libutils/RefBase.cpp
#define INITIAL_STRONG_VALUE (1<<28)
class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
    std::atomic<int32_t>    mStrong;    // 描述强引用计数
    std::atomic<int32_t>    mWeak;     // 描述弱引用计数
    RefBase* const              mBase;       // 指向引用对象的指针
    std::atomic<int32_t>    mFlags;      // 对象生命周期控制的flag:强引用计数控制/弱引用计数控制
    
    // 构造函数给mStrong的初始值不是0,这里很有意思。应该是用于区分对象是否是第一次被sp引用。
    explicit weakref_impl(RefBase* base): mStrong(INITIAL_STRONG_VALUE), mWeak(0), mBase(base), mFlags(0) {}
};

同LightRefBase类,RefBase也提供了相关成员函数incStrong()和decStrong来增加、减少它所维护对象的引用计数。比较不同的一点是RefBase不是直接使用一个整数来描述引用计数,而是新增了一个 weakref_impl 对象来描述强引用计数和弱引用计数。

提供的用于维护引用计数的主要接口:

  • incWeak
  • decWeak
  • attemptIncStrong
  • attemptIncWeak

提供的用于维护引用计数的主要成员:

  • mStrong —— 描述强引用计数
  • mWeak —— 描述弱引用计数
  • mBase —— 指向引用对象的指针
  • mFlags —— 对象生命周期的flag

这里的类组织可能有点迷惑:

  1. RefBase类提供了直接用于操作强引用计数的接口:incStrong和decStrong。
  2. 具体弱引用计数的操作则完全封装到了它的内部类:weakref_type中。
    在这里插入图片描述

强指针实现类——sp

如前文所LightRefBase的智能指针实现类sp,该类也是RefBase的强指针实现类,它的构造函数和析构函数调用调用相应的incStrong()和decStrong()。对应到RefBase的流程如下:

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

    const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
    if (c != INITIAL_STRONG_VALUE)  {
        return;
    }
    int32_t old __unused = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE, std::memory_order_relaxed);
    refs->mBase->onFirstRef();
}
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);
}

这段代码做了三件事:
• 增加弱引用计数
• 增加强引用计数
• 该对象若是第一次被强指针引用,就调用它的onFirstRef函数

注意:由于强引用计数的初始值是INITIAL_STRONG_VALUE,所以如果是第一次被引用,需要减掉这个值。这个值的意义就是用于判断是否第一次被引用,不能是用0来判断是否第一次被引用。

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;
            // The destructor does not delete refs in this case.
        }
    }
    refs->decWeak(id);
}

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;
        }
    }
    const_cast<weakref_impl*&>(mRefs) = nullptr;
}

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) {
            ALOGW("RefBase: Object at %p lost last weak reference "
                    "before it had a strong reference", impl->mBase);
        } else {
            delete impl;
        }
    } else {
        impl->mBase->onLastWeakRef(id);
        delete impl->mBase;
    }
}

decStrong主要做四件事:

  • 减少强引用计数
  • 当减少之后的强引用计数为0时,调用onLastStrongRef函数
  • 当减少之后的强引用计数为0并且引用对象的生命周期控制由强引用计数决定,就delete改引用对象
  • 减少弱引用计数

RefBase的析构函数实现也很简单:

  • 判断引用对象的生命周期是否有弱引用计数决定,如果是并且当前弱引用计数已经为0,就delete内部的mRefs对象。(这样做的原因是因为弱引用计数不为0,有可能有其它的wp在拉着这个对象,所以不能delete mRefs,否则会导致wp出现野指针)

从上文的分析可以看到,incStrong会增加对象的强引用计数和弱引用计数。相应的,decStrong也会减少对象的强引用计数和弱引用计数。

decWeak的主要实现:

  • 减少对象的弱引用计数
  • 如果减少之后的弱引用计数不为0,就直接return
  • 当减少之后的弱引用计数不为0,并且对象的生命周期由强引用计数决定,如果此刻强引用计数是初始值,说明第一次new 引用对象直接是给wp的,此刻什么也没做(PS:这里很容易出现mem leak,所以给了一个warning log);若此刻强引用计数不是初始值,就直接delete weakref_impl对象,因为此时强弱引用计数都为0,已经不需要它了
  • 当减少之后的弱引用高技术不为0,并且对象的生命周期由弱引用计数决定,调用onLastWeakRefs函数,然后delete引用对象(PS:在delete引用对象里面会delete weakref_impl对象)

总结:

  • 当mFlags为OBJECT_LIFETIME_STRONG时,强引用计数为0,就释放对象。
  • 当mFlags为OBJECT_LIFETIME_WEAK 时,弱引用计数为0,就释放对象。
  • RefBase对象的弱引用计数总是大于等于强引用计数

弱指针实现类——wp

template <typename T>
class wp
{
public:
    typedef typename RefBase::weakref_type weakref_type;

    // 构造和析构函数
    inline wp() : m_ptr(nullptr), m_refs(nullptr) { }
    wp(T* other);
    wp(const wp<T>& other);
    explicit wp(const sp<T>& other);
    template<typename U> wp(U* other); 
    template<typename U> wp(const sp<U>& other);
    template<typename U> wp(const wp<U>& other); 
    ~wp();

    // 赋值运算符
    wp& operator = (T* other);
    wp& operator = (const wp<T>& other);
    wp& operator = (const sp<T>& other);
    template<typename U> wp& operator = (U* other);
    template<typename U> wp& operator = (const wp<U>& other);
    template<typename U> wp& operator = (const sp<U>& other);

    sp<T> promote() const;  // promotion to sp

    void clear();
    inline  weakref_type* get_refs() const { return m_refs; }
    inline  T* unsafe_get() const { return m_ptr; }

private:
    T*                          m_ptr;
    weakref_type*   m_refs;
};

template<typename T>
wp<T>::wp(T* other)
    : m_ptr(other)
{
    m_refs = other ? m_refs = other->createWeak(this) : nullptr;
}

template<typename T>
wp<T>::~wp()
{
    if (m_ptr) m_refs->decWeak(this);
}

相比较于强指针,弱指针的不同点:

  • 构造和析构函数只对弱引用计数进行操作
  • 新增了promote()函数,如果需要通过wp访问引用对象,必须调用promote(),返回正确的sp之后才能访问引用对象
  • 成员变量除了m_ptr指向引用对象之外,新增了m_refs对象。(因为只有该对象提供单独弱引用计数的操作

createWeak()就是增加弱引用计数,decWeak上文已经分析过了,就是减少弱引用计数。所以关于wp的分析主要是对它的promote()函数进行分析,即wp是如何转成sp的?

template<typename T>
sp<T> wp<T>::promote() const
{
    sp<T> result;
    if (m_ptr && m_refs->attemptIncStrong(&result)) {
        result.set_pointer(m_ptr);
    }
    return result;
}

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

    // case1
    while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {    // Q1
         if (impl->mStrong.compare_exchange_weak(curCount, curCount+1,
                std::memory_order_relaxed)) {
            break;
        }
    }

    // case2
    if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
        // case2.1
        int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
        if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
            // case2.1.1
            if (curCount <= 0) {
                decWeak(id);
                return false;
            }

            // case2.1.2
            while (curCount > 0) {
                if (impl->mStrong.compare_exchange_weak(curCount, curCount+1,
                        std::memory_order_relaxed)) {
                    break;
                }
            }
        } else {
            // case2.2           
            if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {    // Q2
                // it didn't so give-up.
                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);  // Q3
            }
        }
    }
    if (curCount == INITIAL_STRONG_VALUE) { // Q4
        impl->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
                std::memory_order_relaxed);
    }
    return true;
}

bool RefBase::onIncStrongAttempted(uint32_t flags, const void* /*id*/)
{
    return (flags&FIRST_INC_STRONG) ? true : false;
}

promote函数的实现很简单:若m_ptr指向的对象不为null,调用attemptIncStrong()判断是否可以升级成sp。

attemptIncStrong的主要逻辑(参考左边的注释):

  • Case1:当前对象正在被其它sp引用,此时可以转成sp。

  • Case2:当前对象没有被其它sp引用:

    • Case2.1:当前对象的生命周期受强引用计数控制
      • Case2.1.1:若此刻强引用计数<= 0,说明对象肯定被delete了,此时不能转换。
      • Case2.1.2:若此刻引用计数为INITIAL_STRONG_VALUE,说明对象目前位置还没有被sp引用过,new出来之后就被wp拉着了,此时对象肯定是存在的,所以可以转成sp。
    • Case2.2:当前对象的生命周期受弱引用计数控制,这说明此时对象肯定是存在的,因为当前至少有一个wp在引用它,所以此时可以转成sp。

对于attempIncStrong函数的实现,有几个困惑的地方(如左边注释):

  • Q1:为什么需要一个while循环,再加一个if判断来代码给mCount+1的目的?
    • Ans:可能存在其它线程已经修改了mStrong,所以需要先判断当前mStrong的值和期望值mCount是否相等,如果不相当,将期望值mCount改成mStrong的实际值,并在if中返回false。
  • Q2:onIncStrongAttempted的if判断意义何在?
    • Ans:
  • Q3:为什么需要调用onLastStrongRef,该函数应该是在最后一个sp去掉的时候调用的?
    • Ans:
  • Q4:第一次被强指针引用,为什么不调用onFirstRef?
    • Ans:猜测对于弱引用计数控制的对象,不需要这个概念。

总结

Android智能指针的原理基本上已经分析完了,对于智能指针的使用有几点感想:

  • 当出现bug是由于智能指针拉着资源无法释放导致的问题时,非常不好查,从log中很难找到是哪个指针在拉着对象。
  • 模块之间的接口设计,很可能是不允许是使用智能指针的,只能是标准的指针,这个时候从智能指针获取标准指针传给对应模块,如何保证标准指针指向的内存不被释放?

上文的分析是把debug的代码都去掉后进行分析的,后续如果有时间可以进一步分析debug的模块。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值