android linkToDeath内存泄露分析

Register the recipient for a notification if this binder goes away. //注册binder死亡的通知

public void linkToDeath(@NonNull DeathRecipient recipient, int flags) throws RemoteException;

现场复现

提供给三方应用的sdk中的实现,而对外提供的是一个Manager 单例

java 复制代码
private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
    @Override
    public void binderDied() {
        bindService(); //在service死亡的时候重新拉起保活
    }
};

发现三方应用actiivty在销毁后有内存泄漏,持有引用的是deathReceipent

java 复制代码
private final ServiceConnection mServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
       
        try {
            iBinder.linkToDeath(mDeathRecipient, 0);
        } catch (Throwable e) {
            Log.e(TAG, "linkToDeath fail : ", e);
        }
    }
​​​​​​​}

这里service重新链接的时候,之前已经链接的java对象iBinder会被回收,而注册到iBinder的deathRecipient没有被回收导致内存泄漏

​​​​​​​

​疑问

​​​​​​​iBinder释放的时候是否是unlink DeathRecipient,如果主动释放就不会存在内存泄漏的问题

BinderProxy.java

java 复制代码
​​​​​        public static final long sNativeFinalizer = getNativeFinalizer();
        /* Returns the native free function 随java对象一起释放的native对象 */
        private static native long getNativeFinalizer();
        public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
                BinderProxy.class.getClassLoader(), sNativeFinalizer, NATIVE_ALLOCATION_SIZE);

frameworks/base/core/jni/android_util_Binder.cpp

cpp 复制代码
JNIEXPORT jlong JNICALL android_os_BinderProxy_getNativeFinalizer(JNIEnv*, jclass) {
    return (jlong) BinderProxy_destroy;
}
static void BinderProxy_destroy(void* rawNativeData)
{
    BinderProxyNativeData * nativeData = (BinderProxyNativeData *) rawNativeData;
    LOGDEATH("Destroying BinderProxy: binder=%p drl=%p\n",
            nativeData->mObject.get(), nativeData->mOrgue.get());
    delete nativeData;
    IPCThreadState::self()->flushCommands();
    --gNumProxies;
}

struct BinderProxyNativeData {
    // Both fields are constant and not null once javaObjectForIBinder returns this as
    // part of a BinderProxy.

    // The native IBinder proxied by this BinderProxy.
    sp<IBinder> mObject;

    // Death recipients for mObject. Reference counted only because DeathRecipients
    // hold a weak reference that can be temporarily promoted.
    sp<DeathRecipientList> mOrgue;  // Death recipients for mObject.
};

如上,iBinder销毁只是简单调用了BinderProxyNativeData析构函数,并没有将JavaDeathRecipient从List中移除,

如果发现binderProxy销毁了,并且List不为空,则会发出内存泄露警告

cpp 复制代码
DeathRecipientList::~DeathRecipientList() {
    LOGDEATH("Destroy DRL @ %p", this);
    AutoMutex _l(mLock);

    // Should never happen -- the JavaDeathRecipient objects that have added themselves
    // to the list are holding references on the list object.  Only when they are torn
    // down can the list header be destroyed.
    if (mList.size() > 0) {
        List< sp<JavaDeathRecipient> >::iterator iter;
        for (iter = mList.begin(); iter != mList.end(); iter++) {
            (*iter)->warnIfStillLive();
        }
    }
}
class JavaDeathRecipient : public IBinder::DeathRecipient
{
public:
    JavaDeathRecipient(JNIEnv* env, jobject object, const sp<DeathRecipientList>& list)
        : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)),
          mObjectWeak(NULL), mList(list)
    {
        // These objects manage their own lifetimes so are responsible for final bookkeeping.
        // The list holds a strong reference to this object.
        LOGDEATH("Adding JDR %p to DRL %p", this, list.get());
        list->add(this);

        gNumDeathRefsCreated.fetch_add(1, std::memory_order_relaxed);
        gcIfManyNewRefs(env);
    } 
    
    void clearReference()
    {
        sp<DeathRecipientList> list = mList.promote();
        if (list != NULL) {
            LOGDEATH("Removing JDR %p from DRL %p", this, list.get());
            list->remove(this);
        } else {
            LOGDEATH("clearReference() on JDR %p but DRL wp purged", this);
        }
    }

    void warnIfStillLive() {
        if (mObject != NULL) {
            // Okay, something is wrong -- we have a hard reference to a live death
            // recipient on the VM side, but the list is being torn down.
            JNIEnv* env = javavm_to_jnienv(mVM);
            ScopedLocalRef<jclass> objClassRef(env, env->GetObjectClass(mObject));
            ScopedLocalRef<jstring> nameRef(env,
                    (jstring) env->CallObjectMethod(objClassRef.get(), gClassOffsets.mGetName));
            ScopedUtfChars nameUtf(env, nameRef.get());
            if (nameUtf.c_str() != NULL) {
                ALOGW("BinderProxy is being destroyed but the application did not call "
                        "unlinkToDeath to unlink all of its death recipients beforehand.  "
                        "Releasing leaked death recipient: %s", nameUtf.c_str());
            } else {
                ALOGW("BinderProxy being destroyed; unable to get DR object name");
                env->ExceptionClear();
            }
        }
    }
    }

List添加是在JavaDeathRecipient构造函数中, 移除只有在unlinkToDeath的时候调用clearReference会从list中移除

cpp 复制代码
static jboolean android_os_BinderProxy_unlinkToDeath(JNIEnv* env, jobject obj,
                                                 jobject recipient, jint flags)
{
    jboolean res = JNI_FALSE;
    if (recipient == NULL) {
        jniThrowNullPointerException(env, NULL);
        return res;
    }

    BinderProxyNativeData* nd = getBPNativeData(env, obj);
    IBinder* target = nd->mObject.get();
    if (target == NULL) {
        ALOGW("Binder has been finalized when calling linkToDeath() with recip=%p)\n", recipient);
        return JNI_FALSE;
    }

    LOGDEATH("unlinkToDeath: binder=%p recipient=%p\n", target, recipient);

    if (!target->localBinder()) {
        status_t err = NAME_NOT_FOUND;

        // If we find the matching recipient, proceed to unlink using that
        DeathRecipientList* list = nd->mOrgue.get();
        sp<JavaDeathRecipient> origJDR = list->find(recipient);
        LOGDEATH("   unlink found list %p and JDR %p", list, origJDR.get());
        if (origJDR != NULL) {
            wp<IBinder::DeathRecipient> dr;
            err = target->unlinkToDeath(origJDR, NULL, flags, &dr);
            if (err == NO_ERROR && dr != NULL) {
                sp<IBinder::DeathRecipient> sdr = dr.promote();
                JavaDeathRecipient* jdr = static_cast<JavaDeathRecipient*>(sdr.get());
                if (jdr != NULL) {
                    jdr->clearReference();//从list中移除
                }
            }
        }

        if (err == NO_ERROR || err == DEAD_OBJECT) {
            res = JNI_TRUE;
        } else {
            jniThrowException(env, "java/util/NoSuchElementException",
                              base::StringPrintf("Death link does not exist (%s)",
                                                 statusToString(err).c_str())
                                      .c_str());
        }
    }

    return res;
}

解决方案

在收到binderDied通知后主动调用unlinkToDeath

java 复制代码
private static class DeathRecipient implements IBinder.DeathRecipient {
    private IBinder mBinder;
    private Manager mManager;

    public DeathRecipient(IBinder iBinder, Manager manager) {
        mBinder = iBinder;
        mManager = manager;
    }

    @Override
    public void binderDied() {
        mBinder.unlinkToDeath(this, 0);
        mManager.bindService();
    }
}
​
相关推荐
网络研究院2 小时前
Android 安卓内存安全漏洞数量大幅下降的原因
android·安全·编程·安卓·内存·漏洞·技术
凉亭下2 小时前
android navigation 用法详细使用
android
立秋67893 小时前
Python的defaultdict详解
服务器·windows·python
Indigo_code4 小时前
【数据结构】【链表代码】合并有序链表
数据结构·windows·链表
暮雪倾风4 小时前
【WPF开发】超级详细的“文件选择”(附带示例工程)
windows·wpf
小比卡丘5 小时前
C语言进阶版第17课—自定义类型:联合和枚举
android·java·c语言
前行的小黑炭6 小时前
一篇搞定Android 实现扫码支付:如何对接海外的第三方支付;项目中的真实经验分享;如何高效对接,高效开发
android
何中应6 小时前
如何使用CMD命令启动应用程序(二)
windows·桌面应用·batch命令
sukalot7 小时前
windows C++-使用任务和 XML HTTP 请求进行连接(一)
c++·windows
落落落sss7 小时前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis