Skip to content

RefBase引用计数

追踪 RefBase 的强弱引用控制块、对象生命周期模式、弱引用提升和回调生效时机。

基于android-17.0.0_r1
AndroidBinderRefBaseNative框架源码阅读

RefBase引用计数 ​

RefBase 不是只有一个计数器。每个对象拥有一个 weakref_impl 控制块,分别记录 mStrong、mWeak、生命周期 flags 和回调所需的裸指针。sp<> 管强引用,wp<> 管弱引用;强引用归零通常销毁对象,弱引用还可以继续保存控制块并尝试 promote。Binder 的 BpBinder、BBinder 和对象缓存都依赖这套边沿语义。

本文面向已经读过 BpBinder代理对象、BBinder本地对象 和 IBinder接口 的读者。本文解释强弱计数、首次/最后引用回调、OBJECT_LIFETIME_WEAK 和 attemptIncStrong();不把 sp/wp 写成泛用 C++ 智能指针教程。

1. 控制块 ​

1.1 两个计数 ​

源码文件:system/core/libutils/binder/RefBase.cpp

相关类型:RefBase::weakref_impl

cpp
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;
};

mBase 指向对象本体,控制块即使对象已经析构也可能继续存在,供 wp<> 比较和 promote 使用。默认构造时 mStrong 是 INITIAL_STRONG_VALUE 哨兵,mWeak 为 0。

1.2 计数关系 ​

通常情况下弱计数包含强引用贡献,因此 mWeak >= mStrong(忽略哨兵和并发瞬间)。强计数保护对象字段;弱计数保护控制块和可能的延迟生命周期。

2. 强引用边沿 ​

2.1 首个强引用 ​

cpp
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) {
        refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
                                std::memory_order_relaxed);
        refs->mBase->onFirstRef();
    }
}

第一次强引用会先贡献一个弱引用,再把哨兵转换为真实强计数,最后调用 onFirstRef()。Binder 的 BpBinder::onFirstRef() 就在这个边沿向 IPCThreadState 发送 strong acquire。

2.2 最后强引用 ​

源码文件:system/core/libutils/binder/RefBase.cpp

相关函数:RefBase::decStrong()

cpp
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);
        if ((refs->mFlags.load() & OBJECT_LIFETIME_MASK) ==
                OBJECT_LIFETIME_STRONG) {
            delete this;
        }
    }
    refs->decWeak(id);
}

最后强引用释放先调用 onLastStrongRef(),普通生命周期随后销毁对象,最后再释放该强引用贡献的弱计数。析构可能在任意一次 sp 赋值或函数返回中发生,因此持锁修改 sp 时必须考虑析构副作用。

3. 生命周期模式 ​

3.1 Strong模式 ​

默认 OBJECT_LIFETIME_STRONG:最后强引用销毁对象,控制块通常由最后弱引用释放。对象从未获得过真实强引用就被显式销毁属于危险路径,源码会记录 warning 或触发不变量检查。

3.2 Weak模式 ​

cpp
void RefBase::extendObjectLifetime(int32_t mode) {
    check_not_on_stack(this);
    mRefs->mFlags.fetch_or(mode,
                           std::memory_order_relaxed);
}

OBJECT_LIFETIME_WEAK 让对象保留到最后一个强或弱引用消失;最后弱引用会调用 onLastWeakRef() 后删除对象。BpBinder 使用 weak lifetime,使 handle 表可以在强引用归零后仍安全尝试提升。

3.3 控制块清理 ​

decWeak() 在弱计数归零时,根据 lifecycle flag 删除控制块或对象。Weak 模式下对象和控制块的销毁都由最后弱边沿完成;Strong 模式下对象可能早已在最后强边沿销毁,控制块继续为 wp<> 存活。

4. 弱引用提升 ​

4.1 attemptIncStrong ​

源码文件:system/core/libutils/binder/RefBase.cpp

相关函数:RefBase::weakref_type::attemptIncStrong()

cpp
bool RefBase::weakref_type::attemptIncStrong(const void* id) {
    incWeak(id);
    int32_t curCount = mStrong.load(std::memory_order_relaxed);
    while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
        if (mStrong.compare_exchange_weak(
                curCount, curCount + 1)) break;
    }
    if (curCount <= 0) {
        decWeak(id);
        return false;
    }
    ...
    return true;
}

已有强引用时 promote 是 CAS 增加强计数;普通 Strong lifetime 在所有强引用归零后不能复活;Weak lifetime 则可通过 onIncStrongAttempted() 询问对象是否允许复活。wp<T>::promote() 正是对这个函数的安全包装。

4.2 attemptIncWeak ​

attemptIncWeak() 只有已有弱引用时才成功,不能替代 attemptIncStrong()。ProcessState 的 handle 表用它判断旧 BpBinder 的控制块是否仍可安全复用;这就是代理重建与弱引用提升之间的差异。

5. Binder消费者 ​

5.1 BpBinder ​

BpBinder 的构造、首个强引用、最后强引用和析构分别调用 incWeakHandle、incStrongHandle、decStrongHandle 和 decWeakHandle。RefBase 的边沿回调把 C++ 引用生命周期映射到 Binder driver 引用。

5.2 BBinder ​

BBinder 作为本地 Binder 也继承 RefBase;本地对象的 sp 生命周期控制 C++ 对象,但驱动 node 是否仍有远端引用由 Binder 协议另行维护。

5.3 线程安全 ​

不同 sp<>/wp<> 实例可以并发引用同一对象;同一个 sp<> 或 wp<> 实例的读写没有原子保证。引用计数原子化不等于业务字段自动线程安全。

6. 测试边界 ​

6.1 两种生命周期 ​

源码文件:system/core/libutils/binder/RefBase_test.cpp

参数化 RefBaseBothLifetimes.StrongMoves 分别测试 Strong/Weak lifetime:移动 sp 不增加强计数,最后强引用释放后,Strong 模式对象销毁,Weak 模式仍可由弱引用控制生命周期。

6.2 Promote与比较 ​

RefBaseBothLifetimes.Comparisons 验证对象销毁后弱指针仍可安全比较;ReplacedComparison 在同一地址重新分配新对象,断言旧 wp 不会与新对象相等,证明控制块身份比裸地址更可靠。

6.3 可执行阅读 ​

bash
rg -n "incStrong|decStrong|incWeak|decWeak|attemptIncStrong|attemptIncWeak" \
  system/core/libutils/binder/RefBase.cpp

rg -n "OBJECT_LIFETIME|extendObjectLifetime|onFirstRef|onLastStrongRef|onIncStrongAttempted" \
  system/core/libutils/binder/include/utils/RefBase.h

rg -n "StrongMoves|Comparisons|ReplacedComparison|WeakCopies" \
  system/core/libutils/binder/RefBase_test.cpp

排查 Binder 对象提前析构或旧弱引用误命中时,先区分强计数、弱计数和控制块生命周期,再确认对象是否启用了 Weak lifetime;不要用裸地址或 getStrongCount()(仅调试用途)替代 sp/wp 的正式所有权关系。