智能指针:Binder 中的 sp 与 wp 智能指针如何管理对象生命周期?
说到 Binder 通信,有个绕不开的话题——对象生命周期管理。
你想想看,Binder 是跨进程的。客户端和服务端不在同一个进程里。如果服务端把一个对象传给了客户端,然后服务端自己把这个对象销毁了,客户端那边还在用,那不就崩了吗?
反过来也一样。客户端拿着一个 Binder 引用不放,服务端想销毁对象也销毁不了,内存泄漏就来了。
嗯,Android 的解决方案就是——智能指针。具体来说,是 sp(Strong Pointer)和 wp(Weak Pointer)。
为什么需要两套指针?
我刚开始接触 Binder 源码时,有个疑惑一直解不开:
「一个 sp 不就够了吗?引用计数归零就自动释放,多省心。」
后来在项目里踩了个坑才明白——循环引用。
假设 A 持有了 B 的 sp,B 又持有了 A 的 sp。两个对象的引用计数永远不为 0,谁也释放不了。这就是典型的死锁式内存泄漏。
wp 就是来解决这个问题的。它不增加对象的强引用计数,只保留一个「弱引用」。你可以通过 wp 去尝试获取 sp,但如果对象已经被销毁了,你拿到的就是空指针。
sp 的工作原理
sp 说白了就是一个模板类,内部封装了一个原始指针,外加引用计数管理。
它的行为很简单:
- 构造时:增加对象的强引用计数
- 析构时:减少对象的强引用计数
- 赋值时:先减旧对象的计数,再加新对象的计数
当强引用计数降到 0 时,对象会被立即销毁。
// 伪代码,展示 sp 的核心逻辑
template <typename T>
class sp {
public:
sp(T* other) : m_ptr(other) {
if (m_ptr) m_ptr->incStrong(); // 增加强引用
}
~sp() {
if (m_ptr) m_ptr->decStrong(); // 减少强引用
}
T* get() const { return m_ptr; }
T* operator->() const { return m_ptr; }
private:
T* m_ptr;
};
注意,这里的 incStrong 和 decStrong 是定义在 RefBase 基类中的。所有需要被智能指针管理的对象,都必须继承自 RefBase。
wp 的工作原理
wp 就更有意思了。它只增加弱引用计数,不增加强引用计数。
弱引用计数归零的条件是:强引用计数已经为 0,并且弱引用计数也为 0。
换句话说:
- 强引用计数为 0 → 对象被销毁
- 弱引用计数为 0 → 对象占用的内存被回收
这两步是分开的。对象销毁后,内存可能还在,因为还有 wp 指着它。直到所有 wp 也释放了,内存才真正归还给系统。
// wp 的核心用法
wp<SomeBinderService> weakPtr = service;
// 尝试升级为 sp
sp<SomeBinderService> strongPtr = weakPtr.promote();
if (strongPtr != nullptr) {
// 对象还在,可以安全使用
strongPtr->doSomething();
} else {
// 对象已经被销毁了
// 我曾经在这里吃过亏,忘了检查 nullptr,直接崩了
}
promote() 是关键操作。它尝试把弱引用升级为强引用。如果对象还活着,升级成功,返回一个有效的 sp。如果对象已经死了,返回空 sp。
promote() 一定成功。跨进程场景下,服务端随时可能挂掉或主动销毁对象。每次使用前都要检查返回值。
sp 与 wp 的协作流程
我画了一张图,帮你理清它们的关系:
流程其实很清晰:
sp增加强引用计数,wp增加弱引用计数- 强引用计数归零 → 对象被销毁(析构函数被调用)
- 弱引用计数归零 → 内存被释放
这里有个细节:对象销毁后,wp 的 promote() 会返回空。但 wp 本身还是有效的,直到弱引用计数也归零。
Binder 中的实际应用
在 Binder 驱动层面,sp 和 wp 被大量使用。我举两个典型场景:
| 场景 | 使用方式 | 说明 |
|---|---|---|
| 服务端注册服务 | sp<BnInterface> |
服务端持有强引用,保证对象存活 |
| 客户端获取代理 | wp<BpInterface> |
客户端持有弱引用,避免阻止服务端销毁 |
| 死亡通知 | wp + promote() |
检测服务端是否还活着 |
我记得有一次排查线上问题,发现某个服务端进程退出后,客户端还在疯狂重连。原因就是客户端用 sp 持有了 Binder 代理,导致服务端对象无法完全释放。改成 wp 后,问题就解决了。
sp,结果造成了循环引用。后来统一改成传 wp,在回调内部 promote() 一下,安全又干净。
总结一下
sp 和 wp 是 Binder 对象生命周期管理的基石。它们配合 RefBase 的引用计数机制,解决了跨进程对象释放的难题。
记住三条原则:
- 需要长期持有的对象,用
sp - 只需要临时访问的对象,用
wp+promote() - 永远检查
promote()的返回值
做到这三点,Binder 对象生命周期的问题基本不会找上你。