防止垃圾回收的Binder

7
我想我找到了一个内存泄漏问题,并希望确认我对Android的Binder实现的看法是否正确。在这种情况下,我有一个服务和一个活动,每个进程都有一个。我创建了一个AIDL,允许我通过ipc方法将回调对象从活动传递到服务,然后在服务完成所请求的任务时调用回调。
很长一段时间以来,我一直在想:如果我将一个新的回调对象传递给服务,并且我不在我的活动中保留指向回调对象的指针,为什么垃圾收集器不会收集我的活动进程中的回调?由于似乎没有发生这种情况,JVM如何知道何时收集我的活动中的回调?
我认为答案是Binder系统在活动进程中保留对我的回调的指针,直到相应的服务进程中的回调对象调用其finalize()方法,然后发送消息到活动中释放该指针。这是正确的吗?如果不是,它是如何工作的? 我相信这是正确的,这导致了一个有趣的情况,如果活动中的回调指向非常占用内存的内容,则在服务中的回调被收集之前,它将不会被收集。如果服务的内存不够低,它可能很长时间不会收集回调,而回调可能会在活动中积累,直到活动发生OutOfMemoryError。
2个回答

7

Yury说得基本正确。

我的服务启动一个线程来持有回调函数,当线程完成工作时,它调用回调函数,然后线程结束。当回调被调用时,它可能会在我的Activity中做一点工作,然后返回,在这一点上,我在Activity进程中没有指向回调的指针。

然而,Activity中的回调对象将继续被Android的binder系统指向,直到Service中相应的回调对象被垃圾收集。

如果Activity进程中的回调对象支配了消耗大量内存的其他对象,那么我就在Activity进程中浪费了内存,没有好的理由,甚至可能会得到OutOfMemoryError。 解决方案是在我的回调类中创建一个简单的方法,名为destory() 以清空所有回调的字段,并在完成回调时调用该方法。

如果回调类是非静态内部类,则可以考虑将其更改为静态内部类,并在构造函数中传递父类,这样您也可以在destory()方法中将其置为空。

这带来了一个有趣的想法,如果非静态内部回调类的父类是Activity,并且发生配置更改(如屏幕旋转)在回调通过binder发送但在回调之前发生,则在执行时回调将指向一个旧的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系统会为该线程创建一个本地副本。因此,在你的服务方法返回结果之前,具有对象副本的线程将继续工作。
要检查这一点,只需尝试查看你的服务进程的线程(在DDMS中)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接