Android(安卓)系统C++智能指针----总结
一、前言
想要成为一名优秀的Android开发者, 我们避免不了要深入Android系统进行学习。只有搞明白了Android系统的运行机制、原理,我们才能更加得心应手的开发出性能优越的Android应用。
当然,在进行深入学习之前我们也需要具有一定的基础(例如:C++),想要深入学习Android系统的朋友们还是有必要先去学习C++的。因为在Android底层很多的核心功能都是使用C++开发出来的。
二、什么是智能指针?
使用过C++的朋友肯定都知道C++的指针,使用起来非常方便,功能强大。但是也有不足之处,就是我们必须自己去管理指针的释放,否则就会造成内存泄漏甚至程序崩溃。
那么Android在使用C++的时候, 提供了一个很方便的工具,就是智能指针。目的是帮助我们来进行对象的释放工作。其使用的基本原理就是引用计数法。
我们可以对智能指针做一些总结:
- 一种能够自动维护对象引用计数的技术
- 智能指针一是个对象,而不是一个指针
- 智能指针分为强指针、弱指针
- Android定义了一个公共基类,所有想要使用智能指针的类都要继承它
三、智能指针的公共基类
Android OS 8.0.0_r4
/system/core/include/utils/RefBase.h
class RefBase{public: void incStrong(const void* id) const; // 增加强引用 void decStrong(const void* id) const; // 减少强引用// ... // 内部类表示弱引用类型 class weakref_type { public: RefBase* refBase() const; void incWeak(const void* id); // 增加弱引用 void decWeak(const void* id); // 减少弱引用 bool attemptIncStrong(const void* id); // 尝试获取强引用对象,在弱引用升级强引用时会调用 }; weakref_type* createWeak(const void* id) const; // 创建弱引用 weakref_type* getWeakRefs() const;typedef RefBase basetype;protected使用: RefBase(); virtual ~RefBase();// 虚析构函数,子类也要使用虚析构函数,当基类指针指向派生对象时, 如果不是虚析构函数,delete不会调用派生对象的析构函数,就有可能造成泄露 //标记生命周期的模式 enum { OBJECT_LIFETIME_STRONG = 0x0000, // 生命周期只跟强引用有关, 当强引用为0时释放对象 OBJECT_LIFETIME_WEAK = 0x0001, // 生命周期只跟弱引用有关, 当弱引用为0时释放对象 OBJECT_LIFETIME_MASK = 0x0001 }; void extendObjectLifetime(int32_t mode); // 设置生命周期类型 //! Flags for onIncStrongAttempted() enum { FIRST_INC_STRONG = 0x0001 }; // 生命周期变化时的回调,后面分析中会提到具体的回调时机 virtual void onFirstRef(); virtual void onLastStrongRef(const void* id); virtual bool onIncStrongAttempted(uint32_t flags, const void* id); virtual void onLastWeakRef(const void* id);private: friend class weakref_type; // 声明友源类 class weakref_impl; // 声明类 weakref_impl RefBase(const RefBase& o); // 构造方法 RefBase& operator=(const RefBase& o); // 重载运算符: =private: weakref_impl* const mRefs; // 持有一个类weakref_impl的对象,用来管理强/弱引用计数};
我们上面说到, 智能指针使用的是引用计数法。那么在上面好像并没有看到用来计数的变量。
其实RefBase使用的是weakref_impl来维护强引用计数和弱引用计数的。我们下面看一下weakref_impl的代码。
/system/core/libutils/RefBase.cpp
class RefBase::weakref_impl : public RefBase::weakref_type{public: std::atomic mStrong; // 强引用计数 std::atomic mWeak; // 弱引用计数 RefBase* const mBase; std::atomic mFlags; // 生命周期类型标记#if !DEBUG_REFS explicit weakref_impl(RefBase* base) : mStrong(INITIAL_STRONG_VALUE) // 强引用计数初始化的值, 不是0 , mWeak(0) , mBase(base) , mFlags(0) { }// 非调试模式下都是空实现 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...调试模式#endif};
上面一共看到了三个类,RefBase、weakref_type、weakref_impl。
每一个RefBase对象都包含了一个weakref_impl对象,而weakref_impl继承了weakref_type。
当我们自己的类想要使用智能指针的时候, 只需要继承RefBase就可以了。这样我们的类就拥有了weakref_impl类型的成员变量, 用来维护当前对象的引用计数。当我们把对象赋值给强指针(sp<>)或者弱指针(wp<>)的时候, 就可以不用去管理强弱指针的释放工作了, 不用的时候只需要给强弱指针赋值为NULL就可以了。
四、智能指针的强指针实现类
/system/core/include/utils/StrongPointer.h
templateclass sp {public: inline sp() : m_ptr(0) { } sp(T* other); // NOLINT(implicit) sp(const sp& other); sp(sp&& other); template sp(U* other); // NOLINT(implicit) template sp(const sp& other); // NOLINT(implicit) template sp(sp&& other); // NOLINT(implicit) ~sp(); // Assignment// 重载赋值运算符, 赋值时改变引用数量 sp& operator = (T* other); sp& operator = (const sp& other); sp& operator = (sp&& other); template sp& operator = (const sp& other); template sp& operator = (sp&& other); template 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; } // Operators// 重载运算符, COMPARE是上面定义的宏, 把对应的运算都转换成了m_ptr指针的运算 COMPARE(==) COMPARE(!=) COMPARE(>) COMPARE(<) COMPARE(<=) COMPARE(>=)private: //声明友元类 template friend class sp; template friend class wp; void set_pointer(T* ptr); T* m_ptr; // 强引用指针引用的真实对象};
强指针类的实现也比较简单,就是持有了真实对象的指针, 然后重载了很多运算符, 在构造和析构时分别增加减少引用数量。
我们看一下它的构造和析构方法
templatesp::sp(T* other) : m_ptr(other) { if (other) other->incStrong(this); // 调用存储对象的incStrong()来增加强指针计数}templatesp::~sp() { if (m_ptr) m_ptr->decStrong(this);// 调用存储对象的decStrong()来减少强指针计数}
在构造函数和析构函数中分别调用了incStrong()和decStrong()来增加减少引用计数。这两个方法定义在智能指针的基类RefBase中。我们前面提到过, 所有想要使用智能指针的类都需要继承RefBase。接下来我们去看一下这两个方法的具体实现。
void RefBase::incStrong(const void* id) const{ weakref_impl* const refs = mRefs; // 成员变量mRefs,负责管理强弱引用的计数 refs->incWeak(id); // 每次增加强引用计数都要增加弱引用计数, 所以弱引用计数的值大于等于强引用计数的值 refs->addStrongRef(id); // 非调试模式下是空实现, 可以忽略 const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed); // 强引用计数值增1, 返回老的计数值 if (c != INITIAL_STRONG_VALUE) {// 如果老的计数值不等于初始化的指, 代表不是第一次增加强引用,到这里就直接返回了 return; }// 如果这是第一次增加强引用的值, 需要减去初始值( #define INITIAL_STRONG_VALUE (1<<28) ) , 这样第一次的计数值就是1了 int32_t old = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE, std::memory_order_relaxed); // 上面提到的引用计数变化的回调函数, onFirstRef: 第一次增加强引用计数 refs->mBase->onFirstRef();}
每次调用incStrong来增加强引用计数的时候, 都会同时增加弱引用计数的值。
这里有一点就是, 强引用计数值的初始值不是0, 是INITIAL_STRONG_VALUE (1<<28),这是为了区分是没有增加过, 还是说已经增加过,又减到1了的情况。
void RefBase::decStrong(const void* id) const{ weakref_impl* const refs = mRefs; refs->removeStrongRef(id); // 非调试模式下是空实现, 可以忽略 const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release); // 强引用计数值减1, 返回减1前的值// 如果减1前的值 == 1, 代表当前的强引用计数为0了, 就可以回收了 if (c == 1) { std::atomic_thread_fence(std::memory_order_acquire); // 用来保证线程同步的, refs->mBase->onLastStrongRef(id); // 回调方法 onLastStrongRef, 子类可以复写, 来实现一些业务 int32_t flags = refs->mFlags.load(std::memory_order_relaxed); // 判断生命周期类型的标记 if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) { // 表示生命周期只受强引用计数影响, 当前强引用计数为0, 就需要释放对象内存了 delete this; // 会调用RefBase类的析构函数 // The destructor does not delete refs in this case. } } // Note that even with only strong reference operations, the thread // deallocating this may not be the same as the thread deallocating refs. // That's OK: all accesses to this happen before its deletion here, // and all accesses to refs happen before its deletion in the final decWeak. // The destructor can safely access mRefs because either it's deleting // mRefs itself, or it's running entirely before the final mWeak decrement. refs->decWeak(id);}
当调用decStrong来减少强引用计数的时候, 如果本次减少后, 计数为0了。就会去判断对象生命周期是否是受强引用计数影响, 如果是就会使用delete来回收对象,触发对象的析构函数。
最后会调用decWeak() 来减少弱引用计数
void RefBase::weakref_type::decWeak(const void* id){ weakref_impl* const impl = static_cast(this); const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release); // 弱引用减1 if (c != 1) return; // 不是最后一个弱引用就直接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) {// 异常情况,记录日志 // Decrementing a weak count to zero when object never had a strong // reference. We assume it acquired a weak reference early, e.g. // in the constructor, and will eventually be properly destroyed, // usually via incrementing and decrementing the strong count. // Thus we no longer do anything here. We log this case, since it // seems to be extremely rare, and should not normally occur. We // used to deallocate mBase here, so this may now indicate a leak. ALOGW("RefBase: Object at %p lost last weak reference " "before it had a strong reference", impl->mBase); } else { // 此处释放引用计数对象weakref_impl // ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); delete impl; } } else { // This is the OBJECT_LIFETIME_WEAK case. The last weak-reference // is gone, we can destroy the object. impl->mBase->onLastWeakRef(id); // 如果是受弱引用影响的生命周期, 此次触发RefBase的析构函数, 在RefBase的析构函数中会释放impl delete impl->mBase; }}
我们来看一下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) { // 当弱引用也等于0时,释放它的成员变量 mRefs delete mRefs; } } else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {// 从来没有增加过强引用计数的值, 异常情况 delete mRefs; // We never acquired a strong reference on this object. LOG_ALWAYS_FATAL_IF(mRefs->mWeak.load() != 0, "RefBase: Explicit destruction with non-zero weak " "reference count"); // TODO: Always report if we get here. Currently MediaMetadataRetriever // C++ objects are inconsistently managed and sometimes get here. // There may be other cases, but we believe they should all be fixed. delete mRefs; } // For debugging purposes, clear mRefs. Ineffective against outstanding wp's. const_cast(mRefs) = NULL;}
强引用实现原理小结:
- 强引用指针对象在使用的时候, 只需要初始化成对象就好, 不要初始化成指针类型。我们是在这个对象的成员变量中保存了真实对象的指针。
- 可以把这个对象当作指针来使用, 因为对各种运算符都做了重载。
- 当生命周期受强引用影响时,强引用计数减少为0时会去主动释放当前对象,但是并不会去释放mRefs。
- 当生命周期受弱引用影响时,强引用计数减少为0时,不会触发析构函数回收对象。只有在弱引用计数值也变为0时,才会回收对象。
五、弱引用计数实现原理
弱引用的实现稍微复杂一点, 因为弱引用不可以直接访问它所引用的对象, 只能尝试升级为强引用才能访问它所引用的对象。 升级失败代表对象已被回收, 升级成功则可以正常访问。
弱引用指针的实现类
template class wp{public: typedef typename RefBase::weakref_type weakref_type;// 用weakref_type 表示 RefBase::weakref_type inline wp() : m_ptr(0) { } wp(T* other); // NOLINT(implicit) wp(const wp& other); explicit wp(const sp& other); template wp(U* other); // NOLINT(implicit) template wp(const sp& other); // NOLINT(implicit) template wp(const wp& other); // NOLINT(implicit) ~wp(); // Assignment wp& operator = (T* other); wp& operator = (const wp& other); wp& operator = (const sp& other); template wp& operator = (U* other); template wp& operator = (const wp& other); template wp& operator = (const sp& other); void set_object_and_refs(T* other, weakref_type* refs); // promotion to sp sp promote() const; // Reset void clear(); // Accessors inline weakref_type* get_refs() const { return m_refs; } inline T* unsafe_get() const { return m_ptr; } // Operators COMPARE_WEAK(==) COMPARE_WEAK(!=) COMPARE_WEAK(>) COMPARE_WEAK(<) COMPARE_WEAK(<=) COMPARE_WEAK(>=) inline bool operator == (const wp& o) const { return (m_ptr == o.m_ptr) && (m_refs == o.m_refs); } template inline bool operator == (const wp& o) const { return m_ptr == o.m_ptr; } inline bool operator > (const wp& o) const { return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr); } template inline bool operator > (const wp& o) const { return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr); } inline bool operator < (const wp& o) const { return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr); } template inline bool operator < (const wp& o) const { return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr); } inline bool operator != (const wp& o) const { return m_refs != o.m_refs; } template inline bool operator != (const wp& o) const { return !operator == (o); } inline bool operator <= (const wp& o) const { return !operator > (o); } template inline bool operator <= (const wp& o) const { return !operator > (o); } inline bool operator >= (const wp& o) const { return !operator < (o); } template inline bool operator >= (const wp& o) const { return !operator < (o); }private: template friend class sp; template friend class wp; T* m_ptr; weakref_type* m_refs;};
具体实现跟强引用实现类sp很像, 只是少了 * 和 -> 的运算符重载, 这样我们就没有办法直接通过弱引用来访问真实的对象了。多了一个
sp promote() const;
来实现把弱引用升级为强引用, 这样就可以访问所引用的对象了。
接下来我们先来看一下wp的构成函数。
templatewp::wp(T* other) : m_ptr(other){ if (other) m_refs = other->createWeak(this);}
给指针m_ptr赋值, 然后调用它的createWeak来创建引用对象m_refs
createWeak方法在RefBase中声明, 返回一个weakref_type
RefBase::weakref_type* RefBase::createWeak(const void* id) const{ mRefs->incWeak(id); return mRefs;}
mRefs就是一个weakref_impl的对象, 我们来看一下incWeak是如何增加弱引用计数的
void RefBase::weakref_type::incWeak(const void* id){ weakref_impl* const impl = static_cast(this); impl->addWeakRef(id); // 非调试模式空实现, 忽略 const int32_t c __unused = impl->mWeak.fetch_add(1, std::memory_order_relaxed); ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);}
可以看到实现很简单, 就是给弱引用mWeak的值增1
当我们想要访问弱引用的对象时, 需要先把弱引用升级为强引用才可以访问。
升级为强引用需要调用promote()方法, 下面看一下它的具体实现。
templatesp wp::promote() const{ sp result; if (m_ptr && m_refs->attemptIncStrong(&result)) { result.set_pointer(m_ptr); } return result;}
当m_ptr存在时, 调用了weakref_type的方法attemptIncStrong(), 来尝试升级为强引用。升级成功后, 使用强引用的set_pointer()方法把引用对象赋值给强引用对象,然后返回强引用对象。
templatevoid sp::set_pointer(T* ptr) { m_ptr = ptr; // 因为有重载赋值运算符, 所以会增加强引用。}
下面看一下attemptIncStrong()的具体实现
bool RefBase::weakref_type::attemptIncStrong(const void* id){ incWeak(id); // 弱引用+1 weakref_impl* const impl = static_cast(this); int32_t curCount = impl->mStrong.load(std::memory_order_relaxed);// 取出当前强引用的值 while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {// 当前强引用大于0, 且不是初始值 if (impl->mStrong.compare_exchange_weak(curCount, curCount+1, std::memory_order_relaxed)) { // 强引用+1,保证线程安全,+1成功后,跳出循环。 break; } } // 如果此时的强引用计数指小于等于0, 或者是初始值 if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) { int32_t flags = impl->mFlags.load(std::memory_order_relaxed);// 取出标记位// 对象的强引用不是初始值, 表示该对象已经被回收了, 升级强引用失败, 弱引用-1 if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) { if (curCount <= 0) { decWeak(id); return false; }// 强引用是初始值, 强引用+1, 成功后跳出循环 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 { // this object has an "extended" life-time, i.e.: it can be revived from a weak-reference only. Ask the object's implementation if it agrees to be revived if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) { // it didn't so give-up. decWeak(id); return false; } // 强引用+1 curCount = impl->mStrong.fetch_add(1, std::memory_order_relaxed); // 从弱引用恢复的回调函数 if (curCount != 0 && curCount != INITIAL_STRONG_VALUE) { impl->mBase->onLastStrongRef(id); } } } // curCount 是我们增加之前的指, 如果是初始值, 那么需要减去初始值, 这样强引用计数的值才为1 if (curCount == INITIAL_STRONG_VALUE) { impl->mStrong.fetch_sub(INITIAL_STRONG_VALUE, std::memory_order_relaxed); } return true;}
总结:
- 弱引用不能够直接访问对象,需要尝试升级为强引用才可以访问对象。
- 如果对象生命周期受强引用影响, 那么升级的时候, 如果强引用为0则升级失败, 如果是初始值,则可以升级成功(中间还会判断一次,有没有别的线程回收了该对象)
- 如果生命周期受弱引用影响, 会调用onIncStrongAttempted()来判断是否允许升级强引用。允许则升级强引用, 增加强引用计数,返回true。
- 弱引用之所以不能直接访问对象是因为没有重载*和->运算符。
六、结束语
智能指针的实现还是比较简单的, 可以自己去看一看源代码。这里主要是对自己C++语言的一个检验。里面用到了C++中的一些基础知识: 构造函数、析构函数、模板、友元、运算符重载等。
还牵扯到C++多线程的一些知识点:
原子数据模板类:atomic
C++ 并行编程: 设定 指令执行顺序
typedef enum memory_order { memory_order_relaxed, // 不对执行顺序做保证 memory_order_acquire, // 本线程中,所有后续的读操作必须在本条原子操作完成后执行 memory_order_release, // 本线程中,所有之前的写操作完成后才能执行本条原子操作 memory_order_acq_rel, // 同时包含 memory_order_acquire 和 memory_order_release memory_order_consume, // 本线程中,所有后续的有关本原子类型的操作,必须在本条原子操作完成之后执行 memory_order_seq_cst // 全部存取都按顺序执行 } memory_order;
最后附上可以查看源码的链接:
版本: android os 8.0.0_r4
RefBase.h
RefBase.cpp
StrongPointer.h
更多相关文章
- Android那点事-系列之(一)Parcelable和Serializable的区别与使用
- jni开发之一
- Handler消息机制的原理及应用场景
- [转]Android(安卓)init 启动过程分析
- 阿里巴巴的FastJson数据解析介绍
- 【Android】从libavenhancements.so的调用分析高通封装
- Android(安卓)Native OpenGL应用前后台切换的总结
- Activity源码之Android(安卓)6.0权限相关完全解析
- Android(安卓)binder from Top to Bottom