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
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 首个强引用
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()
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模式
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()
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 可执行阅读
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 的正式所有权关系。
