Binder 阻止垃圾收集

问题描述 投票:0回答:2

我认为我找到了内存泄漏,并想确认我认为关于 Android Binder 的实现方式的真实情况。在本例中,我有一个服务和一个活动,每个都在自己的进程中。我创建了一个 AIDL,它允许我通过 ipc 方法将回调对象从 Activity 传递到 Service,然后在 Service 完成请求的任务时调用回调。

很长一段时间我都在想:如果我将一个新的 Callback 对象传递给 Service 并且我没有在 Activity 中保留指向 Callback 对象的指针为什么垃圾收集器不继续收集 Callback我的 Activity 进程? 既然这似乎没有发生,那么 JVM 如何知道何时对我的 Activity 中的回调进行垃圾回收。

我认为答案是Binder系统在Activity进程中保留了一个指向我的Callback的指针,直到Service进程中对应的Callback对象调用了其finalize()方法,然后该对象向Activity发送消息释放该指针。 这是正确的吗?如果没有的话怎么运作?

我相信是的,它会导致有趣的情况,如果活动中的回调指向内存密集型的东西,那么在收集服务中的回调之前,它不会被收集。如果服务内存不低,它可能会在很长一段时间内不会收集回调,并且回调可能会在活动中累积,直到活动中出现 OutOfMemoryError 为止。

android ipc android-binder
2个回答
7
投票

尤里几乎是正确的。

我的服务启动一个保存回调的线程,当线程完成其工作时,它会调用回调并结束线程。当调用回调时,它可能会在我的 Activity 中执行一点点工作,然后返回,此时我的 Activity 进程中没有指向回调的指针。

但是Activity中的回调对象会继续被Android的binder系统指向,直到Service中对应的回调对象被垃圾回收为止。

如果 Activity 进程中的回调对象支配了一些消耗大量内存的其他对象,那么我就无缘无故地浪费了 Activity 进程中的内存,甚至可能会出现 OutOfMemoryError。 解决方案是在我的回调类中创建一个名为

destroy()
的简单方法,以清空所有回调的字段,并在完成回调后调用该方法。

如果回调类是非静态内部类,您可能需要考虑将其更改为静态内部类,并在构造函数中传入父类,这样您也可以在

destroy()
方法中将其清空。

这带来了一个有趣的想法,如果非静态内部回调类的父类是 Activity,并且在通过绑定器发送回调之后但在回调之前发生配置更改(例如屏幕旋转),那么执行时回调将指向旧的 Activity 对象!

更新:我在 Binder.java 中发现了这段代码,当然它被禁用了,但如果他们在 Javadocs 中提到这种东西那就太好了。

    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Binder> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Binder class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

1
投票

如果我正确理解 Binder 如何工作,您的情况下的问题如下。对于每个传入的传入呼叫,您的服务都会创建一个单独的线程。当您将对象传递给该线程时,您的 Binder 系统会为该线程创建对象的本地副本。因此,在您的 Service 方法返回结果之前,具有对象副本的线程将继续工作。

要检查这一点,只需尝试查看服务进程的线程(在 DDMS 中)。

© www.soinside.com 2019 - 2024. All rights reserved.