我认为我追踪了内存泄漏并希望确认我认为Android的Binder如何实现的可能性。在这种情况下,我有一个服务和一个活动,每个都在他们自己的过程中。我创建了一个AIDL,它允许我通过ipc方法将Call中的Callback对象传递给Service,然后在服务完成所请求的任务时调用回调。
很长一段时间我都在想:如果我将一个新的Callback对象传递给Service,并且我没有在我的Activity 中保留一个指向Callback对象的指针,为什么垃圾收集器不会继续执行在我的Activity进程中收集回调?由于这似乎没有发生,JVM如何知道何时在我的Activity中垃圾回收回调。
我认为答案是Binder系统在Activity进程中保留一个指向我的Callback的指针,直到Service进程中相应的Callback对象调用了finalize()方法,然后向Activity发送一条消息以释放指针。 这是对的吗?如果不是,它是如何工作的?
我相信它是并且它会导致一个有趣的情况,如果活动中的Callback指向非常占用内存的东西,则在收集服务中的Callback之前不会收集它。如果服务内存不足,它可能不会长时间收集回调,并且回调可能只是在活动中积累,直到活动中出现OutOfMemoryError。
答案 0 :(得分:6)
我的服务启动一个保存回调的线程,当线程完成其工作时,它调用回调并且线程结束。当调用回调时,它可以在我的Activity中执行一些工作,然后返回,此时我的Activity进程中没有指向回调的指针。
然而,Android的活页夹系统将继续指向Activity中的回调对象,直到服务中相应的回调对象被垃圾收集。
如果Activity进程中的回调对象占用了一些消耗大量内存的其他对象,那么我在我的Activity进程中浪费内存是没有充分理由的,甚至可能得到一个OutOfMemoryError。 解决方案是在我的回调类中创建一个名为destory()
的简单方法,以取消所有回调的字段,并在完成回调时调用该方法。
如果回调类是非静态内部类,您可能需要考虑将其更改为静态内部类并在构造函数中传入父类,这样您也可以在{{1方法。
这引出了一个有趣的想法,如果非静态内部回调类的父类是一个Activity,并且在通过绑定器发送回调之后但在调用之前发生配置更改(例如屏幕旋转)那么回调将在执行时指向一个旧的Activity对象!
更新:我在Binder.java中发现了这个代码,当然它被禁用了,但如果他们在Javadocs中提到这种东西会很好。
destory()
答案 1 :(得分:1)
如果我理解Binder如何正常工作,您的案例中的问题如下。对于每个传入来电,您的服务都会创建一个单独的线程将对象传递给此线程时,Binder系统会为该线程创建对象的本地副本。因此,在您的Service方法返回结果之前,具有该对象副本的线程将继续工作。
要检查这一点,只需尝试查看服务进程的线程(在DDMS中)。