目录
- 一、智能指针的使用
-
- [1.1 强引用 sp 基本使用](#1.1 强引用 sp 基本使用)
- [1.2 弱引用 wp 的基本使用](#1.2 弱引用 wp 的基本使用)
- [1.3 智能指针的两种模式](#1.3 智能指针的两种模式)
- [1.4 Android12 中的一点变化](#1.4 Android12 中的一点变化)
- 二、智能指针的原理与源码分析
-
- [2.1 基本原理](#2.1 基本原理)
- [2.2 强引用源码分析](#2.2 强引用源码分析)
- [2.3 弱引用源码分析](#2.3 弱引用源码分析)
- 三、使用场景
-
- [3.1 前提步骤:自定义类继承 RefBase](#3.1 前提步骤:自定义类继承 RefBase)
- [3.2 sp(Strong Pointer,强指针)](#3.2 sp(Strong Pointer,强指针))
- [3.3 wp(Weak Pointer,弱指针)](#3.3 wp(Weak Pointer,弱指针))
- 四、总结
|-------------------------|
| Android 平台智能指针使用与分析 |
一、智能指针的使用
1.1 强引用 sp 基本使用
强引用简化了指针的使用,让我们不用考虑野指针问题与回收内存操作,其基本使用如下:
c
// 0. 需要使用智能指针的类必须继承自 RefBase
class X:public RefBase
{
void test()
{
//......
}
}
{ // 作用域开始
// 1.被管理的指针
X* p = new X();
// 2.构建强引用
sp<X> spX(p); // sp<X> spX = new X() 这样也行,注意不要写成 sp<X> spX = new sp<X>()
// 3.通过强引用,操作指针指向的对象
spX->test();
} // 作用域结束,调用强引用对象的析构函数,释放掉 p 指针指向的内存
可以看出,在使用上,sp 就和指针一样,而且 sp 代为管理了 p 指针的回收操作,不需要我们手动回收内存。
1.2 弱引用 wp 的基本使用
默认情况下,弱引用用于记录一个指针值,不能通过弱引用来访问弱引用内部指针指向的对象,也就是说不能通过弱引用来调用成员函数或读写成员变量。要想使用弱引用内部指针所指向的对象,需首先将弱引用升级为强指针(通过 wp 类所提供的 promote() 方法)后,在通过强指针操作目标对象。
弱引用内部指针所指向的对象可能在其它地方被销毁了,如果该对象已经被销毁,wp 的 promote() 方法返回的 sp 对象的内部指针为空,这样就能避免出现地址访问错误的情况。其使用方法如下:
c
{ // 作用域开始
// 1.被管理的指针
X* p = new X();
// 2.构建一个弱引用
wp<X> wpX(p);
// 3. 不能直接通过弱引用使用对象,升级为强指针再使用
sp<X> spX2 = wpx.promote()
if(spX2 != NULL) // spX2 与 NULL 不是同一类型, 这里重载了 !=,实际比较的是 spX2 内部指针与 NULL 是否相等
{
spX2->test();
}
}
需要注意的是 if(spX2 != NULL) ,spX2 与 NULL 不是同一类型, 这里重载了 !=,实际比较的是 spX2 内部指针与 NULL 是否相等
1.3 智能指针的两种模式
智能指针有两种模式: OBJECT_LIFETIME_STRONG 和 OBJECT_LIFETIME_WEAK, 可以通过void RefBase::extendObjectLifetime(int32_t mode)函数切换模式,可传入的参数有:
c
// 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
};
这个函数通常在被管理的目标类的构造函数中调用:
c
// frameworks/native/libs/binder/BpBinder.cpp
BpBinder::BpBinder(Handle&& handle)
: mStability(0),
mHandle(handle),
mAlive(true),
mObitsSent(false),
mObituaries(nullptr),
mTrackedUid(-1) {
// 调用函数修改智能指针模式
extendObjectLifetime(OBJECT_LIFETIME_WEAK);
}
绝大多数情况下都是使用默认的 OBJECT_LIFETIME_STRONG 模式,两种模式的差异,在下一节的源码分析中来做解释。
1.4 Android12 中的一点变化
在 Android12 之前,要创建一个被sp管理的ProcessState对象,需要两步操作,核心是手动用new创建「裸指针」,再传给sp的构造函数封装。
传统用法代码示例(Android12 之前)
c
// 步骤1:手动用 new 创建 ProcessState 裸指针(无任何人管理,手动创建,手动销毁风险高)
ProcessState* raw_ptr = new ProcessState(driver); // raw_ptr 是裸指针,没有引用计数
// 步骤2:将裸指针传给 sp 的构造函数,封装成 sp 智能指针(开始管理生命周期)
sp<ProcessState> gProcess = sp<ProcessState>(raw_ptr);
// 等价于简化写法:sp<ProcessState> gProcess(new ProcessState(driver));
这就像:
你自己去工厂(new)造了一个物品(ProcessState对象,裸指针),这个物品没有专人看管,丢了没人管(忘记delete就会内存泄漏),摔坏了也没人负责(野指针访问)。
你找到一个「专业保管员」(sp智能指针),把这个物品交给保管员,从此保管员负责物品的存放、维护(引用计数),物品没人需要时,保管员会自动把它销毁(delete),不用你操心。
传统用法的潜在问题
外部接触到了「裸指针」(raw_ptr),如果有人不小心手动delete了裸指针,或者把裸指针传给多个sp,会导致重复释放或野指针,引发崩溃。
两步操作略显繁琐,且不符合现代 C++「尽量避免直接使用裸指针」的规范。
以上是 Android 平台智能指针的传统用法,Android12 中更新了一种全新的使用方法:sp类提供了一个静态成员函数make(),它把传统用法的「两步操作」(new裸指针 + 封装sp)合并成了一步,而且全程在sp类内部完成,外部完全接触不到裸指针。
c
// 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> 对象,其内部 new 一个 ProcessState 对象,在将成员变量 m_ptr 指针指向它
gProcess = sp<ProcessState>::make(driver);
//......
return gProcess;
}
上面的代码的 make 函数中,构建一个 sp对象,其内部构建了一个新对象 new ProcessState(driver),再将 sp 的成员变量 m_ptr 指针指向这个新对象。 总结一下就是用 make 代替了之前的 new 操作。
- make()是sp< ProcessState>的静态函数(所以用::调用,不用先创建 sp 对象)。
- make()函数内部会自动执行new ProcessState(driver),创建ProcessState裸指针(这一步在sp内部完成,外部看不到)。
- make()函数内部会把这个裸指针封装成一个sp< ProcessState>智能指针(给裸指针加上引用计数,开始管理生命周期)。
- make()函数最后返回这个封装好的sp< ProcessState>对象,赋值给gProcess。
二、智能指针的原理与源码分析
2.1 基本原理
在 C++ 中,对象可以分为栈对象与堆对象:
c
void test() {
Sheep sheep; //栈对象,作用域结束后,会调用对象的析构函数
Sheep* pSheep = new Sheep(); //堆对象,需要手动释放
delete pSheep; //释放 pSheep 指向的对象
pSheep = 0; //将pSheep指向NULL,防止造成野指针问题
}
在一个作用域中,对象分为了两类:
- 栈对象:直接声明的对象,例如上面的
Sheep sheep; - 堆对象:通过指针和 new 构建的对象,例如上面的
Sheep* pSheep = new Sheep()
当栈对象所在的作用域结束时,栈对象的析构函数会被执行,智能指针就是利用了这一点来实现内存自动释放功能的:
- 引用对象是一个栈对象,内部有一个指针,指向被管理的对象
- 被管理对象内部保存有一个引用计数值
- 当增加一个引用对象时,引用计数值加 1
- 当引用对象所在作用域执行完后,会执行到引用对象的析构函数,在析构函数中,引用计数减 1,执行完减 1 操作后,如果引用计数值等于 0,那么释放引用内部指针指向的对象
以上就是智能指针的基本原理,实际的源码会复杂一些:
2.2 强引用源码分析
在 1.1 节的示例代码中,我们定义了一个 X 类,X 类继承自 RefBase,当执行到 X* p = new X() 代码时,会执行到父类 RefBase 的构造函数:
c
// 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)
{
}
-
mBase:
RefBase* const类型指针,指向需要管理的对象,也就是 RefBase 构造函数中传入的 this,即当前对象指针 -
mStrong:强引用计数,初始值为 INITIAL_STRONG_VALUE 即 0x1000000
-
mWeak:弱引用计数,初始值为 0
-
mFlags:一个标记值,标识是强引用控制还是弱引用控制,默认是 0 ,表示强引用控制,其可选值如下:
c
// 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);
}
当 1.1 节的示例代码执行到 sp< X> spX( p); 时,就会调用到 sp 的构造函数:
c
// 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); // 增加强引用计数
}
}
主要执行了如下操作:
- m_ptr 指向被管理对象
- incStrong 增加强引用计数
接着我们来看一看 incStrong 的具体实现:
c
// 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);
}
incStrong 中:
- 调用 incWeak ,弱引用计数 mWeak 加 1
- 强引用计数 mStrong 加 1
- 如果是第一次引用,mStrong 减去初始值 INITIAL_STRONG_VALUE
通过以上代码的分析,我们可以看出强引用 sp 的结构如下

当强引用所在作用域执行完后,就会执行强引用对象的析构函数了:
这里分为两种情况:
- 强引用控制模式 OBJECT_LIFETIME_STRONG
- 弱引用控制模式 OBJECT_LIFETIME_WEAK
先分析强引用控制模式 OBJECT_LIFETIME_STRONG 下的 sp 析构流程:
c
// system/core/libutils/include/utils/StrongPointer.h
template<typename T>
sp<T>::~sp() {
if (m_ptr)
m_ptr->decStrong(this); //执行被管理对象的 decStrong 函数
}
//接着调用 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
}
// mStrong 减 1 h 后如果等于 0, 就会 delete 目标对象 X,会执行到目标对象的析构函数
RefBase::~RefBase()
{
int32_t flags = mRefs->mFlags.load(std::memory_order_relaxed);
//强引用走 else 分支
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) {
//打印一些信息,不用管
//......
}
//内部 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;
}
}
当 sp 析构时:
- 调用 decStrong 函数
- 强引用计数 mStrong 减 1,减 1 后,如果 mStrong 等于 0,就会 delete 目标对象 X
- 接着调用 decWeak 函数
- 弱引用计数 mWeak 减 1,减 1 后,如果 mWeak 等于 0,就会 delete 管理对象 weakref_impl
总结一下,在 强引用控制模式 下:
- 被管理的对象必须继承自 RefBase
- RefBase 的成员
weakref_impl* const mRefs记录了强引用计数值与弱引用计数值 - 当执行sp 的构造函数时,会调用到 weakref_impl 类的 incStrong 函数, 强引用计数值加 1,弱引用计数值加 1
- 当 sp 所在作用域指针完毕,执行 sp 的析构函数
强引用减 1,减 1 后强引用计数值为 0,则回收 sp 内部指针 m_ptr 指向的内存(即管理的对象 weakref_impl)
弱引用减 1,减 1 后弱引用计数值为 0,则回收 RefBase 的成员
weakref_impl* const mRefs指向的内存
可以看出,在强引用控制下,强引用 sp 管理的内存的回收与否是由强引用计数与弱引用计数共同决定的,mStrong 为零时回收目标对象,mWeak 为零时回收管理对象,只有两者同时等于 0,内存才能被完整回收掉。
接着我们分析弱引用控制模式 OBJECT_LIFETIME_WEAK 下的 sp 析构流程:
c
// 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 下 sp 的两个特点:
- 是否回收对象由弱引用计数 mWeak 是否为 0 单独决定,当 mWeak 等于 0 时,不论 mStrong 是否为 0,目标对象和管理对象均会被回收掉,也就是说,sp 退化为一个普通的引用计数智能指针,引用计数值是 mWeak
- 目标对象和内部管理对象的回收时间点不同于强引用控制
2.3 弱引用源码分析
上一节的示例程序执行到 wp< X> wpX§; 时,就会执行指针 wp 的构造函数:
c
// 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 源码的实现:
c
// 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
接下来示例代码执行到弱引用转强引用的代码:
c
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
//拿到内部管理对象
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;
}
promote() 函数处理了多种情况与边界条件,无论哪种情况,总结一下就是弱引用计数加 1 强引用计数加 1
当弱引用所在作用域执行完后,就会执行弱引用对象的析构函数了:
c
// 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; //回收目标对象,进而执行到 RefBase 的析构函数
}
}
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) {
delete mRefs;
}
} else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
//打印一些信息,不用管
//......
}
//内部 mRefs 指针赋值为 nullptr,防止野指针
const_cast<weakref_impl*&>(mRefs) = nullptr;
}
析构函数中,弱引用计数减 1 ,减1后,如果弱引用计数等于 0 则:
- 强引用控制: 回收管理对象
weakref_impl,目标对象并没有被回收,说明在强引用模式下 wp 不能单独使用,必须结合在作用域结束前与一个 sp 联系起来(构造函数,promote 函数等) - 弱引用控制: 回收目标对象,进而执行到 RefBase 的析构函数,析构函数中回收管理对象 weakref_impl,说明在弱引用模式下,wp 是可以单独使用的,进化为一个普通的引用计数指针,引用计数值是 mWeak。
三、使用场景
3.1 前提步骤:自定义类继承 RefBase
使用 sp/wp 管理对象的必要条件是自定义类继承RefBase,示例:
c
#include <utils/RefBase.h>
using namespace android; // 简化命名空间,安卓Native开发常用
// 自定义类,继承RefBase,才能被sp/wp管理
class MyObject : public RefBase {
public:
MyObject() {
ALOGD("MyObject 构造"); // 安卓日志,需链接log库
}
~MyObject() {
ALOGD("MyObject 析构");
}
void doSomething() {
ALOGD("MyObject 执行操作");
}
};
3.2 sp(Strong Pointer,强指针)
sp是最常用的安卓智能指针,持有对象的强引用,自动维护强引用计数,核心作用是自动释放对象,避免内存泄漏。
核心特性
- 持有强引用,增加对象的强引用计数。
- 当sp对象销毁(超出作用域、被重置)时,自动减少强引用计数。
- 强引用计数为0时,自动调用delete销毁被管理的对象。
- 用法类似普通指针,支持->、*操作符访问对象成员。
常用使用方法(代码示例)
c
#include <utils/StrongPointer.h> // sp定义在此头文件(实际可直接包含RefBase.h,会间接引入)
#include <android/log.h>
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "SmartPointerDemo", __VA_ARGS__)
int main() {
// 1. 初始化sp对象(三种常用方式)
// 方式1:直接构造,管理新创建的对象
sp<MyObject> obj1 = new MyObject();
// 方式2:使用make_sp(安卓高版本支持,更安全,避免裸指针直接暴露)
// sp<MyObject> obj1 = make_sp<MyObject>();
// 方式3:空初始化,后续赋值
sp<MyObject> obj2;
obj2 = obj1; // 赋值时,强引用计数+1(此时obj1和obj2的强引用计数都是2)
// 2. 访问对象成员(和普通指针用法一致)
obj1->doSomething();
obj2->doSomething();
// 3. 查看强引用计数(通过RefBase的getStrongCount()方法)
ALOGD("强引用计数:%d", obj1->getStrongCount()); // 输出:2
// 4. 重置sp对象(释放强引用,强引用计数-1)
obj2.clear(); // obj2变为空,强引用计数变为1
ALOGD("重置obj2后,强引用计数:%d", obj1->getStrongCount()); // 输出:1
// 5. 手动赋值为nullptr,效果和clear()一致
obj1 = nullptr; // 强引用计数变为0,对象被自动析构
ALOGD("程序结束");
return 0;
}
运行结果(日志)
c
plaintext
MyObject 构造
MyObject 执行操作
MyObject 执行操作
强引用计数:2
重置obj2后,强引用计数:1
MyObject 析构
程序结束
无需手动调用delete,sp会在强引用计数为0时自动销毁对象。
sp是值类型,拷贝 / 赋值都会增加强引用计数,多个sp可以共享同一个对象。
3.3 wp(Weak Pointer,弱指针)
wp是弱指针,持有对象的弱引用,不增加强引用计数,不会阻止对象被销毁,核心作用是解决循环引用问题,同时避免野指针。
核心特性
- 持有弱引用,仅增加弱引用计数,不影响对象的生命周期。
- 无法直接通过wp访问对象成员(->、*操作符不可用),必须先 提升为
sp(promote()方法 才能访问。 promote()方法:尝试将弱引用提升为强引用,返回sp对象:- 若对象仍存在(强引用计数 > 0),返回有效的sp,强引用计数 + 1。
若对象已销毁(强引用计数 = 0),返回空sp(nullptr)。 - 适用于 "只需要知道对象是否存在,不希望阻止对象销毁" 的场景。
常用使用方法
c
#include <utils/WeakPointer.h> // wp定义在此头文件
int main() {
// 1. 先创建sp管理对象,保证对象存在
sp<MyObject> sp_obj = new MyObject();
ALOGD("强引用计数:%d,弱引用计数:%d",
sp_obj->getStrongCount(), sp_obj->getWeakCount()); // 1, 0
// 2. 初始化wp对象,关联到sp管理的对象(弱引用计数+1)
wp<MyObject> wp_obj = sp_obj;
ALOGD("强引用计数:%d,弱引用计数:%d",
sp_obj->getStrongCount(), sp_obj->getWeakCount()); // 1, 1
// 3. 尝试将wp提升为sp,访问对象
sp<MyObject> sp_from_wp = wp_obj.promote();
if (sp_from_wp != nullptr) { // 判空:避免访问已销毁的对象
ALOGD("wp提升为sp成功,访问对象");
sp_from_wp->doSomething();
ALOGD("提升后强引用计数:%d", sp_from_wp->getStrongCount()); // 2
} else {
ALOGD("wp提升为sp失败,对象已销毁");
}
// 4. 释放sp,对象销毁(强引用计数归0)
sp_obj = nullptr;
ALOGD("释放sp后,弱引用计数:%d", wp_obj.promote() ? 1 : 0); // 0(对象已销毁)
// 5. 再次尝试提升wp,返回空
sp<MyObject> sp_from_wp2 = wp_obj.promote();
if (sp_from_wp2 == nullptr) {
ALOGD("对象已销毁,wp提升失败");
}
return 0;
}
运行结果(日志)
c
MyObject 构造
强引用计数:1,弱引用计数:0
强引用计数:1,弱引用计数:1
wp提升为sp成功,访问对象
MyObject 执行操作
提升后强引用计数:2
MyObject 析构
释放sp后,弱引用计数:0
对象已销毁,wp提升失败
核心场景:解决循环引用问题
循环引用是sp的常见坑(两个对象互相持有对方的sp,导致强引用计数无法归 0,内存泄漏),wp是解决该问题的关键。
循环引用问题示例(内存泄漏)
c
// 互相持有sp,导致循环引用
class B; // 前向声明
class A : public RefBase {
public:
sp<B> b_sp; // 持有B的sp
~A() { ALOGD("A 析构"); }
};
class B : public RefBase {
public:
sp<A> a_sp; // 持有A的sp
~B() { ALOGD("B 析构"); }
};
int main() {
sp<A> a = new A();
sp<B> b = new B();
a->b_sp = b;
b->a_sp = a;
// 释放外部sp后,内部互相持有,强引用计数无法归0,对象无法析构(内存泄漏)
a = nullptr;
b = nullptr;
ALOGD("程序结束");
return 0;
}
解决循环引用(将其中一个 sp 改为 wp)
c
class B;
class A : public RefBase {
public:
sp<B> b_sp;
~A() { ALOGD("A 析构"); }
};
class B : public RefBase {
public:
wp<A> a_wp; // 改为wp,避免循环强引用
~B() { ALOGD("B 析构"); }
};
int main() {
sp<A> a = new A();
sp<B> b = new B();
a->b_sp = b;
b->a_wp = a;
a = nullptr;
b = nullptr; // 强引用计数归0,对象正常析构
ALOGD("程序结束");
return 0;
}
运行结果(日志,无内存泄漏)
c
A 析构
B 析构
程序结束
使用注意事项(避坑关键)
被管理的对象必须继承RefBase:否则无法使用 sp/wp,编译报错或运行时崩溃。
避免sp循环引用:两个对象互相持有sp会导致内存泄漏,使用wp打破循环。
wp访问对象前必须promote()并判空:否则可能访问已销毁的对象,导致野指针崩溃。
不要用 sp/wp 管理非堆对象:sp/wp 内部会调用delete,若管理栈对象(局部变量),会导致双重释放崩溃。
c
// 错误示例:管理栈对象
MyObject obj;
sp<MyObject> sp_obj = &obj; // 运行时会崩溃(delete栈对象)
安卓版本兼容性:make_sp是高版本安卓(API 28+)支持的,低版本需直接使用new构造 sp。
头文件依赖:开发时需包含<utils/RefBase.h>,链接libutils.so和liblog.so(日志相关)。
四、总结
-
- 安卓智能指针核心是 sp(强指针)和wp(弱指针),基于引用计数管理内存,依赖RefBase基类。
-
- sp:持有强引用,自动销毁对象,解决内存泄漏,是日常开发的首选,用法接近普通指针。
-
- wp:持有弱引用,不影响对象生命周期,用于解决循环引用,访问前需通过promote()提升为sp并判空。
-
- 核心避坑:避免sp循环引用,不管理栈对象,wp访问前必须判空。
-
- 适用场景:安卓 Native 层(Framework、HAL、驱动配套工具)开发,替代手动new/delete,提升内存管理安全性。