我们通常的代码都是使用java完成的,而java中有自己的内存回收机制,当对象不再使用时,会自动将内存回收,我们是不需要关注对象的回收的。而C++则不是这样,当我们new了一个对象后,当不需要再使用它的时候必须通过delete将内存回收。引用计数法是使用一个变量记录当前对象被引用的个数,当引用数为0时,表明该对象没有被引用了,即可以被回收了。但是引用计数法有个问题就是当存在互相引用的时候,会认为两个对象都存在引用关系,从而不会回收。但是Android智能指针通过另一种方式实现引用计数法,并没有直接通过对象本身进行引用,从而实现了内存的自动释放并且不会有相互依赖导致无法释放的问题。
1 2 3 4 5 6 7 8
| int test() { A *a = new A; A a1;
delete a; return 0; }
|
C++有两种创建对象的方式,通过new创建的对象位于堆内存中,使用结束后必须通过delete回收。直接声明的则位于栈内存中,作用域结束后就自动回收了。因此可以将二者进行结合,实现堆内存中的对象也能直接回收。如示例,定义了A类,持有了B的引用,在A的析构函数中将B进行释放。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class B;
class A { public: A(B* b) { mRef = b; }
~A() { delete mRef; }
private: B* mRef; };
|
那么在实际使用中,就可以实现B对象的自动释放。
1 2 3 4 5 6 7 8
| int test() { B *b = new B; A a(b);
return 0; }
|
Android智能指针也是使用的上述的逻辑,通过栈内存的自动释放带动堆内存的自动释放,只是Android做的更加完善一些。它加入了引用计数,通过引用数来判断该对象是否需要被释放。因此,引用计数属性就需要定义在被应用的对象中,RefBase就是被抽取出来的基类,实现了引用计数的逻辑。 如果你的对象想要通过智能指针管理内存的释放,那么它必须继承自efBase,然后使用sp和wp进行引用。
RefBase
RefBase是所有引用类的基类,也就是说想要通过智能指针管理内存释放的类,都必须继承RefBase。从下面的定义可以看到,RefBase内部的方法incStrong/decStrong就是主要用于引用计数的。其内部还定义了一个weakref_type的弱引用类型,对应的也有incWeak和decWeak。从这里我们可以看到Android的智能指针是有两种类型的,强引用和弱引用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
class RefBase { public: void incStrong(const void* id) const; void incStrongRequireStrong(const void* id) const; void decStrong(const void* id) const; void forceIncStrong(const void* id) const;
class weakref_type { public: RefBase* refBase() const; void incWeak(const void* id); void incWeakRequireWeak(const void* id); void decWeak(const void* id); bool attemptIncStrong(const void* id); bool attemptIncWeak(const void* id); };
protected: RefBase(); virtual ~RefBase();
private: friend class weakref_type; class weakref_impl; RefBase(const RefBase& o);
private: weakref_impl* const mRefs; };
|
sp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| template<typename T> class sp { public: sp(T* other); sp(const sp<T>& other); ... inline T& operator* () const { return *m_ptr; } inline T* operator-> () const { return m_ptr; } inline T* get() const { return m_ptr; } ...
private: T* m_ptr; }
|
sp是智能指针的强引用使用方式,也就是通过sp来实现了内存的自动释放。对于我们使用而言,如我们需要一个User对象,那么我们new User之后,使用sp<User>包裹一下即可,后续也不需要关注什么时候需要释放User了。实际使用方式可以直接如下:
1 2 3 4 5
| User *mUser = new User; sp<User> p_user(mUser);
p_user->setAge(10);
|
实现流程
1 2 3 4 5 6 7 8 9 10 11 12
|
template<typename T> sp<T>::sp(T* other) : m_ptr(other) {
if (other) { check_not_on_stack(other); other->incStrong(this); } }
|
当使用sp的时候,会在其构造方法中给m_ptr赋值为实际对象的引用,方便后续调用该引用的方法和属性等,同时增加了强引用的个数。这里的other是实际的引用对象,也就是RefBase的子类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
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, memory_order_relaxed); refs->mBase->onFirstRef(); }
|
实际的操作都是在weakref_impl中完成的,它是定义在RefBase中的一个属性,主要作用就是用于记录引用个数的,在引用对象创建的时候同步创建weakref_impl。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
RefBase::RefBase(): mRefs(new weakref_impl(this)) { }
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;
explicit weakref_impl(RefBase* base) : mStrong(INITIAL_STRONG_VALUE) // 初始值INITIAL_STRONG_VALUE , mWeak(0) // 弱引用个数为0 , mBase(base) , mFlags(OBJECT_LIFETIME_STRONG) // 当前属于强引用生命周期 { } }
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); }
|
在sp的构造方法中,将引用对象的强引用和弱引用的个数都增加了1,当该对象被很多个sp使用时,它的强引用和弱引用的个数都会有多个,因此在sp的析构函数中,应该会对引用个数进行判断,如果没有了引用个数,应该将该对象进行回收。
1 2 3 4 5 6
| template<typename T> sp<T>::~sp() { if (m_ptr) m_ptr->decStrong(this); }
|
判断有误,在sp的析构函数中并没有判断引用个数,只是简单的减少了强引用的个数,因此,那些判断的逻辑应该是藏在了RefBase本身中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| 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); }
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; } }
|
生命周期
在sp中,每次创建sp的时候,都会给引用对象的强引用数和弱引用数+1,然后在自动销毁sp的时候,在它的析构函数中将引用对象的强引用和弱引用数-1。因此,如果强引用数不为0,则弱引用数一定不为0。
在前面说过创建RefBase的时候,会同时创建一个weakref_impl对象,该impl用于存储强引用和弱引用的个数的。同时,impl还有一个属性flag,默认取值为OBJECT_LIFETIME_STRONG,即强引用生命周期,它还有一个取值为OBJECT_LIFETIME_WEAK弱引用声明周期。该值是用于控制引用类型什么情况下被回收的。
通常情况下,不需要主动控制对象的生命周期类型,即默认的OBJECT_LIFETIME_STRONG就可以了,它会在没有强引用的时候销毁。但是,如果你的对象比较重要,你想要让它一直不销毁,直到所有的强引用和弱引用都不存在的时候才去销毁的话,可以在构造方法中调用extendObjectLifetime(OBJECT_LIFETIME_WEAK)将其切换成弱引用生命周期。
wp
前面提到的sp是强引用,也是大部分场景下使用的引用,强引用的特点就是当前被引用的对象不会被回收掉,每次都可以直接拿来使用。弱引用与强引用的使用方式是一样的,直接使用wp<>引用原对象即可,它的特点是当前引用的对象可能已经被回收额,因此,如果想要通过弱引用访问对象的属性或方法时,必须先判断对象是否为空。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| template <typename T> class wp { public: static inline wp<T> fromExisting(T* other); wp(T* other); ... sp<T> promote() const; ... inline T* unsafe_get() const { return m_ptr; }
private: T* m_ptr; weakref_type* m_refs; };
|
wp并没有重写*和->操作符,因此无法直接对引用对象进行操作,它提供了一个unsafe_get方法来获取到引用对象,从方法名可以看到这个方法是不安全的,就是这个方法返回的T*可能是已经被回收过了的。另外还提供了一个promote方法,用于将弱引用转换成强引用。
1 2 3 4 5 6
| template<typename T> wp<T>::wp(T* other) : m_ptr(other) { m_refs = other ? m_refs = other->createWeak(this) : nullptr; }
|
构造方法也没做什么,就是将引用赋值给m_ptr,然后调用了引用对象的createWeak,最终的结果也就是将弱引用值-1。
1 2 3 4 5
| RefBase::weakref_type* RefBase::createWeak(const void* id) const { mRefs->incWeak(id); return mRefs; }
|
注意这里的区别:强引用sp的构造方法中,将强引用数和弱引用数都+1操作了,而弱引用wp的构造方法中只给弱引用数+1了,也就是说,实际的引用对象中:弱引用数 >= 强引用数。
1 2 3 4 5
| template<typename T> wp<T>::~wp() { if (m_ptr) m_refs->decWeak(this); }
|
析构函数中也没有别的操作,只是将弱引用数-1。具体逻辑前面已经讲过了,弱引用数-1后,判断是否还有弱引用,然后对应的判断是否需要回收对象。
wp访问引用对象只有两种方式,一种是通过unsafe_get方法获取到引用对象本身,另一种方式就是通过promote方法将弱引用转换成强引用。
1 2 3 4 5 6 7 8 9 10 11
| 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; }
|
这里就比较讲究了,调用的sp的空构造方法,也就是说实际上是没有引用对象的,也没有给强引用数+1。只有当引用对象还存在的时候,并且attemptIncStrong返回true的时候才会设置引用对象,此时才会升级成功。因此,通过promote升级为强指针后仍要判断是否为空。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| 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);
while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) { if (impl->mStrong.compare_exchange_weak(curCount, curCount+1, std::memory_order_relaxed)) { break; } }
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; } } 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); } } }
if (curCount == INITIAL_STRONG_VALUE) { impl->mStrong.fetch_sub(INITIAL_STRONG_VALUE, std::memory_order_relaxed); } return true; }
|
这里升级为强引用时判断了引用对象的生命周期类型,如果是强引用生命周期并且未被回收,则直接升级成功,强引用和弱引用数都+1处理。如果是弱引用生命周期类型,则交给对象本身去决定是否能够升级成功。
1 2 3 4
| bool RefBase::onIncStrongAttempted(uint32_t flags, const void* ) { return (flags&FIRST_INC_STRONG) ? true : false; }
|
因为我们是弱引用生命周期才走到这个判断,所以这里一直返回false。也就是说,对于强引用生命周期的对象的wp弱引用来说,是有可能通过promote升级为强引用sp来使用的;但是对于弱引用生命周期的对象来说,是永远无法promote的(除非重写onIncStrongAttempted方法)。从上面的这些分析也可以看出来,promote的作用并不是将wp转成sp的,而是要将弱引用生命周期的对象转成强引用生命周期的。
总结下来就是:promote默认情况下是不会改变对象的生命周期类型的,只是会创建出一个新的sp来使用,并且这个新的sp还不一定有值。因此,一般情况下可以不需要管这个方法,直接使用unsafe_get即可。
LightRefBase
轻量级RefBase,只支持sp强引用,不支持wp弱引用。比较简单,就是本身存储一个变量记录强引用的个数。如果只使用强引用的话,可以直接继承LightRefBase,否则继承RefBase。
使用方式
1 2 3 4 5 6 7 8 9
| sp<User> p_user = new User; int age = p_user->age;
wp<User> w_user = new User; if(w_user.unsafe_get() != nullptr) { int age1 = w_user.unsafe_get().age; }
|
总结
引用数:使用sp强引用时,会给强引用数和弱引用数同时+1;使用wp弱引用时,只会给弱引用数+1。
生命周期:对象继承自Refbase从而支持强引用和弱引用,同时,对象本身也是区分为强生命周期和弱生命周期的。强生命周期在没有了强引用时会被销毁掉;弱引用生命周期会在没有了弱引用时销毁。因为一个对象的弱引用数>=强引用数,所以可以看出来弱引用生命周期的对象存在的时间会久一些。默认是强引用生命周期。
使用方式:强引用可以直接通过*、->、get方式来调用引用对象的属性和方法;弱引用可以通过unsafe_get来访问。