本文根据 aosp android12_r28 分支剖析。
C/C++ 指针在运用上首要存在以下两类难点:
- 野指针问题
- C++ 的熟练工一般会把刚声明的指针赋值为 NULL。声明而未赋值的指针,有可能指向了一个未知的地址,成为野指针,操作这个指针可能会带来程序过错与溃散。
- 将方针 delete 后,C++ 的熟练工会将指针赋值为 NULL,否则这个指针也会成为野指针
- 当多个指针都指向了方针 A,当某个地方将方针 A delete 后,操作其他地方的指针,便是对一个非法的内存进行操作了
- 当运用了 malloc/new 分配了内存,都需求运用 free/delete 收回这块内存,当程序变得复杂起来,很多时分,程序员就会忘掉收回内存,导致内存走漏。
在 Android 中完成了一套完善的智能指针机制来简化 C++ 中指针的运用,以躲避以上的两类难点。
1.智能指针的运用
在 Android 中,智能指针是一个类/方针,分为强引证 sp 与弱引证 wp,以下是 wp 与 sp 的运用示例:
// 需求运用智能指针的类有必要承继自 RefBase
class X:public RefBase
{
void test()
{
//......
}
}
int main()
{
X* p = new X();
//构建一个强引证
sp<A> spX(p); // sp<A> spX = new X() 这样也行,留意不要写成 sp<A> spX = new sp<A>()
//构建一个弱引证
wp<A> wpX(p); // 也能够 wp<A> wpX(spX);
//经过强引证运用方针
spX->test();
//经过弱引证运用方针
//不能直接经过弱引证运用方针,升级为强指针再运用
sp<A> spX2 = wpx.promote()
if(spX2 != NULL) // spX2 与 NULL 不是同一类型, 这里重载了 !=,实际比较的是 spX2 内部指针与 NULL 是否持平
{
spX2->test();
}
//离开引证效果域后,自动收回 p 指向的内存
}
强引证与一般意义的智能指针概念相同,经过引证计数来记载有多少运用者在运用一个方针,假如一切运用者都抛弃了对该方针的引证,则该方针地点的内存将被自动收回。其运用办法如下:
// 0. 需求运用智能指针的类有必要承继自 RefBase
class X:public RefBase
{
void test()
{
//......
}
}
{ // 效果域开端
// 1.被办理的指针
X* p = new X();
// 2.构建强引证
sp<A> spX(p);
// 3.经过强引证,操作指针指向的方针
spX->test();
} // 效果域完毕,调用强引证方针的析构函数,开释掉 p 指针指向的内存
默许状况下,弱引证用于记载一个指针值,不能经过弱引证来拜访弱引证内部指针指向的方针,也便是说不能经过弱引证来调用其内部指针指向的方针的成员函数或拜访其成员变量。要想拜访弱引证内部指针所指向的方针,需首先将弱引证升级为强指针(经过 wp 类所提供的 promote() 办法),弱引证内部指针所指向的方针可能在其它地方被销毁了,假如该方针现已被销毁,wp 的 promote() 办法将返回空指针,这样就能避免出现地址拜访过错的状况。其运用办法如下:
{ // 效果域开端
// 1.被办理的指针
X* p = new X();
// 2.构建一个弱引证
wp<A> wpX(p);
// 3. 不能直接经过弱引证运用方针,升级为强指针再运用
sp<A> spX2 = wpx.promote()
if(spX2 != NULL) // spX2 与 NULL 不是同一类型, 这里重载了 !=,实际比较的是 spX2 内部指针与 NULL 是否持平
{
spX2->test();
}
}
以上是 Android 渠道智能指针的传统用法,Android12 中更新了一种全新的运用办法:
// frameworks/native/libs/binder/ProcessState.cpp
sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault)
{
//......
static sp<ProcessState> gProcess;
//......
// 在 make 内部会运用参数 driver,new 一个 ProcessState 方针
// 构建一个 sp<ProcessState> 方针,其内部指针 m_ptr 履行刚 new 的 ProcessState 方针
gProcess = sp<ProcessState>::make(driver);
//......
return gProcess;
}
2.智能指针的原理
在 C++ 中,方针能够分为栈方针与堆方针:
void test() {
//栈方针,test 函数履行完毕后,会调用方针的析构函数
Sheep sheep;
Sheep* pSheep = new Sheep();//堆方针,需求手动开释
delete pSheep; //开释pSheep指向的方针
pSheep = 0;//将pSheep指向NULL,避免造成野指针}
}
当栈方针地点的效果域完毕时,栈方针的析构函数会被履行,智能指针便是利用了这一点来完成内存自动开释功能的:
- 引证方针是一个栈方针
- 一个引证方针对应一个指针,引证方针内部保存有指针的值与一个引证计数值
- 当增加指针的引证时,引证计数值加 1
- 当引证方针地点效果域履行完后,会履行到引证方针的析构函数,在析构函数中,引证计数减 1,履行完减 1 操作后,假如引证计数值等于 0,那么开释引证方针内部指针指向的内存
以上便是智能指针的基本原理,接下来咱们再来看看源码是如何完成的:
2.1 强引证源码剖析
在上一节的示例代码中,咱们界说了一个 X 类,X 类承继自 RefBase,当履行到 X* p = new X()
代码时,会履行 RefBase 的结构函数:
// system/core/libutils/RefBase.cpp
RefBase::RefBase()
: mRefs(new weakref_impl(this))
{
}
//上面代码会履行到 weakref_impl 结构函数
explicit weakref_impl(RefBase* base)
: mStrong(INITIAL_STRONG_VALUE)
, mWeak(0)
, mBase(base)
, mFlags(0)
{
}
RefBase 结构函数会先 new 一个 weakref_impl 方针出来然后赋值给 mRefs 成员。weakref_impl 内部保存了智能指针需求的重要数据:
- mStrong:强引证计数,当这个值为 0 时,需求收回内部指针指向的内存,初始值为 INITIAL_STRONG_VALUE 即 0x1000000
- mWeak:弱引证计数,初始值为 0
- mBase:
RefBase* const
类型指针,指向需求办理的方针,也便是 RefBase 结构函数中传入的 this,当时方针指针 - mFlags:一个符号值,标识是强引证操控还是弱引证操控,默许是 0 ,表示强引证操控,其可选值如下:
// system/core/libutils/include/utils/RefBase.h enum { //强引证操控 OBJECT_LIFETIME_STRONG = 0x0000, //弱引证操控 OBJECT_LIFETIME_WEAK = 0x0001, //OBJECT_LIFETIME_MASK 是一个掩码值,后边会运用 flag&OBJECT_LIFETIME_MASK 的形式来获取 flag 的最低位数值 OBJECT_LIFETIME_MASK = 0x0001 }; // system/core/libutils/RefBase.cpp // mFlags 的值一般在方针方针(示例中的 X 类)的结构函数中经过 extendObjectLifetime 函数修改 void RefBase::extendObjectLifetime(int32_t mode) { mRefs->mFlags.fetch_or(mode, std::memory_order_relaxed); }
当上一节的示例代码履行到 sp<A> spX(p);
时,就会调用到 sp 的结构函数:
// system/core/libutils/include/utils/StrongPointer.h
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other) { // m_ptr 是一个指针,指向需求办理的方针
if (other) {
check_not_on_stack(other); // 确保 other 是一个堆方针
other->incStrong(this); // 增加强引证计数
}
}
接着咱们来看一看 incStrong 的详细完成:
// system/core/libutils/RefBase.cpp
void RefBase::incStrong(const void* id) const
{
weakref_impl* const refs = mRefs; //拿到内部数据方针
refs->incWeak(id); //增加弱引证计数
refs->addStrongRef(id); // 由 DEBUG_REFS 操控,release 版别什么也不做
//强引证计数加 1
const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
#if PRINT_REFS
ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
if (c != INITIAL_STRONG_VALUE) { //判断是否为第一次引证
return;
}
//第一次引证把初始化值减掉
int32_t old __unused = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE, std::memory_order_relaxed);
// A decStrong() must still happen after us.
ALOG_ASSERT(old > INITIAL_STRONG_VALUE, "0x%x too small", old);
//给被办理方针的回调接口,第一次引证时回调
refs->mBase->onFirstRef();
}
// system/core/libutils/RefBase.cpp
void RefBase::weakref_type::incWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->addWeakRef(id); // 由 DEBUG_REFS 操控,release 版别什么也不做
//弱引证计数加 1
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);
}
当强引证地点效果域履行完后,就会履行强引证方针的析构函数了:
这里分为两种状况:
- 强引证操控形式 OBJECT_LIFETIME_STRONG
- 弱引证操控形式 OBJECT_LIFETIME_WEAK
咱们先剖析强引证操控形式 OBJECT_LIFETIME_STRONG 下的 sp 析构流程:
// system/core/libutils/include/utils/StrongPointer.h
template<typename T>
sp<T>::~sp() {
if (m_ptr)
m_ptr->decStrong(this); //履行被办理方针的 decStrong 函数
}
// system/core/libutils/RefBase.cpp
void RefBase::decStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->removeStrongRef(id); // 由 DEBUG_REFS 操控,release 版别什么也不做
// 强引证计数减 1
// 留意返回值是履行减 1 之前的值
const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);
#if PRINT_REFS
ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
LOG_ALWAYS_FATAL_IF(BAD_STRONG(c), "decStrong() called on %p too many times",
refs);
if (c == 1) { // mStrong 等于 0 了,要履行内存收回操作了
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) { //OBJECT_LIFETIME_STRONG 形式下
delete this; //收回方针方针内存
}
}
refs->decWeak(id); //弱引证减 1,收回内部方针 weakref_impl
}
// 这里 delete 了被办理方针,会履行到被办理方针的析构函数
RefBase::~RefBase()
{
int32_t flags = mRefs->mFlags.load(std::memory_order_relaxed);
//走 else 分支
if ((flags & OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) { //假如是弱引证操控进入 if
if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) { //弱引证计数为 0
delete mRefs; //整理内部指针
}
} else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
//打印一些信息,不用管
//......
}
//内部 mRefs 指针赋值为 nullptr,避免野指针
const_cast<weakref_impl*&>(mRefs) = nullptr;
}
//接着履行 decWeak
void RefBase::weakref_type::decWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->removeWeakRef(id);
// 弱引证计数减 1
// 留意返回值是履行减 1 之前的值
const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release);
LOG_ALWAYS_FATAL_IF(BAD_WEAK(c), "decWeak called on %p too many times",
this);
if (c != 1) return; //mWeak == 0 了往下履行
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) //sp 结构函数中对 mStrong 赋值了,走 else
== INITIAL_STRONG_VALUE) {
ALOGW("RefBase: Object at %p lost last weak reference "
"before it had a strong reference", impl->mBase);
} else {
delete impl; // 收回 RefBase 的内部方针 weakref_impl
}
} else { //弱引证操控
impl->mBase->onLastWeakRef(id);
delete impl->mBase;
}
}
总结一下,在强引证操控形式下:
- 被办理的方针有必要承继自 RefBase
- RefBase 的成员
weakref_impl* const mRefs
内部记载了强引证计数值与弱引证计数值 - 当履行 sp 的结构函数时(即完成一次对指针的引证),强引证计数值加 1,弱引证计数值加 1
- 当 sp 地点效果域指针完毕,履行 sp 的析构函数
- 强引证减 1,减 1 后强引证计数值为 0,则收回 sp 内部指针 m_ptr 指向的内存(即 sp 办理的方针)
- 弱引证减 1,减 1 后弱引证计数值为 0,则收回 RefBase 的成员
weakref_impl* const mRefs
指向的内存
能够看出,在强引证操控下,内存的收回与否是有强引证与弱引证一起决议的。
接着咱们剖析弱引证操控形式 OBJECT_LIFETIME_WEAK 下的 sp 析构流程:
// system/core/libutils/include/utils/StrongPointer.h
template<typename T>
sp<T>::~sp() {
if (m_ptr)
m_ptr->decStrong(this); //履行被办理方针的 decStrong 函数
}
// system/core/libutils/RefBase.cpp
void RefBase::decStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->removeStrongRef(id); // 由 DEBUG_REFS 操控,release 版别什么也不做
// 强引证计数减 1
// 留意返回值是履行减 1 之前的值
const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);
#if PRINT_REFS
ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
LOG_ALWAYS_FATAL_IF(BAD_STRONG(c), "decStrong() called on %p too many times",
refs);
if (c == 1) { // mStrong 等于 0 了,要履行内存收回操作了
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) { //弱引证形式下不进入 if
delete this;
}
}
refs->decWeak(id); //弱引证减 1,
}
//接着履行 decWeak
void RefBase::weakref_type::decWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->removeWeakRef(id);
// 弱引证计数减 1
// 留意返回值是履行减 1 之前的值
const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release);
LOG_ALWAYS_FATAL_IF(BAD_WEAK(c), "decWeak called on %p too many times",
this);
if (c != 1) return; //mWeak == 0 了往下履行
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) else
== INITIAL_STRONG_VALUE) {
ALOGW("RefBase: Object at %p lost last weak reference "
"before it had a strong reference", impl->mBase);
} else {
delete impl; // 收回 RefBase 的内部方针 weakref_impl
}
} else { //弱引证操控,走 else 分支
impl->mBase->onLastWeakRef(id);
delete impl->mBase; //delete 被办理方针
}
}
// 这里 delete 了被办理方针,会履行到被办理方针的析构函数
RefBase::~RefBase()
{
int32_t flags = mRefs->mFlags.load(std::memory_order_relaxed);
if ((flags & OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) { //假如是弱引证操控进入 if
if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) { //弱引证计数为 0
delete mRefs; //整理内部办理方针指针
}
} else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
}
//内部 mRefs 指针赋值为 nullptr,避免野指针
const_cast<weakref_impl*&>(mRefs) = nullptr;
}
弱引证操控形式 OBJECT_LIFETIME_WEAK 下的两个差异点:
- 是否收回方针又弱引证计数 mWeak 决议
- 方针方针和内部办理方针的收回时刻点不同于强引证操控
2.2 弱引证源码剖析
当咱们上一节的示例程序履行到 wp<A> wpX(p);
时,就会履行到指针 wp 的结构函数:
// system/core/include/utils/RefBase.h
template<typename T>
wp<T>::wp(T* other)
: m_ptr(other) //保存指针到 m_ptr
{
m_refs = other ? m_refs = other->createWeak(this) : nullptr; //增加弱引证计数,wp 内部的 m_refs 指向 createWeak 的返回值(即 RefBase 内部的 mRefs)
}
结构函数内部完成了三件事:
- 保存指针到 m_ptr
- 调用 createWeak 增加弱引证计数
- 将 wp 内部的 m_refs 指向 createWeak 的返回值(即 RefBase 内部的 mRefs)
接着咱们来看看 createWeak
源码的完成:
// system/core/libutils/RefBase.cpp
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
mRefs->incWeak(id); //调用 weakref_impl 的 incWeak
return mRefs;
}
// system/core/libutils/RefBase.cpp
void RefBase::weakref_type::incWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->addWeakRef(id); // 由 DEBUG_REFS 操控,release 版别什么也不做
//弱引证计数加 1
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);
}
以上代码首要操作便是给弱引证计数加 1
接下来履行到弱引证转强引证的代码:
sp<A> spX2 = wpx.promote()
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;
}
//处理了多种状况,总结一下便是弱引证加1 强引证加1
bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
incWeak(id); //弱引证加 1,在示例中,弱引证计数现已为 2
//拿到内部办理方针
weakref_impl* const impl = static_cast<weakref_impl*>(this);
//拿到强引证计数值
int32_t curCount = impl->mStrong.load(std::memory_order_relaxed);
ALOG_ASSERT(curCount >= 0,
"attemptIncStrong called on %p after underflow", this);
while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
//强引证计数加 1
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) {
//强引证计数加 1
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)) { //始终是 false
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);
}
}
}
impl->addStrongRef(id);
#if PRINT_REFS
ALOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount);
#endif
// 减掉初始值
if (curCount == INITIAL_STRONG_VALUE) {
impl->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
std::memory_order_relaxed);
}
return true;
}
template<typename T>
void sp<T>::set_pointer(T* ptr) {
m_ptr = ptr;
}
处理了多种状况,中心流程便是弱引证加 1 强引证加 1。
当弱引证地点效果域履行完后,就会履行弱引证方针的析构函数了:
// system/core/include/utils/RefBase.h
template<typename T>
wp<T>::~wp()
{
if (m_ptr) m_refs->decWeak(this); //指针内部方针的 decWeak 函数
}
//接着履行 decWeak
void RefBase::weakref_type::decWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->removeWeakRef(id);
// 弱引证计数减 1
// 留意返回值是履行减 1 之前的值
const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release);
LOG_ALWAYS_FATAL_IF(BAD_WEAK(c), "decWeak called on %p too many times",
this);
if (c != 1) return; // mWeak == 0 往下履行
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; // 收回 RefBase 的内部方针 weakref_impl
}
} else { //弱引证操控
impl->mBase->onLastWeakRef(id);
delete impl->mBase;
}
}
析构函数中,弱引证计数减 1 后等于 0 则:
- 强引证操控: 收回 RefBase 的内部方针 weakref_impl
- 弱引证操控: 收回 impl->mBase,也便是办理的方针方针,从而履行到 RefBase 的析构函数
总结
智能指针的基本原理和运用是十分简略的,Android 渠道智能指针的完成略显繁琐,首要原因是增加了弱引证支持以及对各类边界状况的处理。咱们需求熟练掌握智能指针的运用,了解智能指针初始化和析构的主干流程,假如在实践中遇到相关问题,再深入源码,剖析各类边界状况。
参考资料
- Android智能指针RefBase、sp、wp解析
- android系统中心机制 根底(01)智能指针wp & sp
- Android根底–智能指针
关于
我叫阿豪,2015 年本科结业于国防科学技术大学指挥信息系统专业,结业后从事信息化装备的研发作业,首要研讨方向是 Android Framework 与 Linux Kernel。
假如你对 Android Framework 感兴趣或者正在学习 Android Framework,能够重视我的微信大众号和抖音,我会继续共享我的学习经历,协助正在学习的你少走一些弯路。学习过程中假如你有疑问或者你的经历想要共享给我们能够增加我的微信,我拉你进技术交流群。