Android 平台智能指针使用与分析

目录

  • 一、智能指针的使用
    • [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 操作。

  1. make()是sp< ProcessState>的静态函数(所以用::调用,不用先创建 sp 对象)。
  2. make()函数内部会自动执行new ProcessState(driver),创建ProcessState裸指针(这一步在sp内部完成,外部看不到)。
  3. make()函数内部会把这个裸指针封装成一个sp< ProcessState>智能指针(给裸指针加上引用计数,开始管理生命周期)。
  4. 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是最常用的安卓智能指针,持有对象的强引用,自动维护强引用计数,核心作用是自动释放对象,避免内存泄漏。

核心特性

  1. 持有强引用,增加对象的强引用计数。
  2. 当sp对象销毁(超出作用域、被重置)时,自动减少强引用计数。
  3. 强引用计数为0时,自动调用delete销毁被管理的对象。
  4. 用法类似普通指针,支持->、*操作符访问对象成员。

常用使用方法(代码示例)

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是弱指针,持有对象的弱引用,不增加强引用计数,不会阻止对象被销毁,核心作用是解决循环引用问题,同时避免野指针。
核心特性

  1. 持有弱引用,仅增加弱引用计数,不影响对象的生命周期。
  2. 无法直接通过wp访问对象成员(->、*操作符不可用),必须先 提升为sp(promote()方法 才能访问。
  3. promote()方法:尝试将弱引用提升为强引用,返回sp对象:
  4. 若对象仍存在(强引用计数 > 0),返回有效的sp,强引用计数 + 1。
    若对象已销毁(强引用计数 = 0),返回空sp(nullptr)。
  5. 适用于 "只需要知道对象是否存在,不希望阻止对象销毁" 的场景。

常用使用方法

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(日志相关)。


四、总结

    1. 安卓智能指针核心是 sp(强指针)和wp(弱指针),基于引用计数管理内存,依赖RefBase基类。
    1. sp:持有强引用,自动销毁对象,解决内存泄漏,是日常开发的首选,用法接近普通指针。
    1. wp:持有弱引用,不影响对象生命周期,用于解决循环引用,访问前需通过promote()提升为sp并判空。
    1. 核心避坑:避免sp循环引用,不管理栈对象,wp访问前必须判空。
    1. 适用场景:安卓 Native 层(Framework、HAL、驱动配套工具)开发,替代手动new/delete,提升内存管理安全性。

相关推荐
有位神秘人2 小时前
Android中BottomSheetDialog的折叠、半展开、底部固定按钮等方案实现
android
阿猿收手吧!2 小时前
【C++】实现自旋锁:三种高效实现与实战指南
服务器·网络·c++
LeeeX!2 小时前
YOLOv13全面解析与安卓平台NCNN部署实战:超图视觉重塑实时目标检测的精度与效率边界
android·深度学习·yolo·目标检测·边缘计算
dongdeaiziji2 小时前
Android 图片预加载和懒加载策略
android
代码游侠2 小时前
C语言核心概念复习(三)
开发语言·数据结构·c++·笔记·学习·算法
明洞日记2 小时前
【软考每日一练030】软件维护:逆向工程与再工程的区别与联系
c++·软件工程·软考·逆向工程
郝学胜-神的一滴2 小时前
深入Linux网络编程:accept函数——连接请求的“摆渡人”
linux·服务器·开发语言·网络·c++·程序人生
茉莉玫瑰花茶2 小时前
C++ 17 详细特性解析(3)
开发语言·c++
C+-C资深大佬3 小时前
C++多态
java·jvm·c++