使者远程服务导致内存泄漏(Messenger to Remote Service Causing M

2019-06-27 15:16发布

我有与通信的应用的Service中使用的远程过程Messenger接口。 这里的事情是如何设置的基本结构:

  • 该应用程序生成需要访问服务的几个“操作”的对象。
  • 每个“操作”包含一个Handler包在Messenger用于从接收的响应数据回Service
  • 当操作执行时,它包裹其MessengerIntent并调用startService()将该消息传递到所述远程服务
  • 远程服务执行基于所述的参数的一些工作Intent ,然后返回通过发送所述响应MessageMessenger该操作。

这里存在于运行的基本代码:

public class SessionOperation {

    /* ... */

    public void runOperation() {
        Intent serviceIntent = new Intent(SERVICE_ACTION);
        /* Add some other extras specific to each operation */
        serviceIntent.putExtra(Intent.EXTRA_EMAIL, replyMessenger);

        context.startService(serviceIntent);
    }

    private Handler mAckHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //Process the service's response
        }
    };
    protected Messenger replyMessenger = new Messenger(mAckHandler);
}

和服务是如何构成的片段(这基本上是一个IntentService不关机,当队列为空):

public class WorkService extends Service {
    private ServiceHandler mServiceHandler;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //If intent has a message, queue it up
        Message msg = mServiceHandler.obtainMessage();
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);

        return START_STICKY;
    }

    private void onHandleIntent(Intent intent) {
        Messenger replyTarget = intent.getParcelableExtra(Intent.EXTRA_EMAIL);

        /* Do some work */

        Message delivery = Message.obtain(...);
        replyTarget.send(delivery);
    }
}

这一切工作飞驰很好。 我可以从几个不同的应用程序发送万吨的操作相同的服务,他们都处理并发送其响应拿捏到位。 然而...

我注意到,如果应用程序运行足够长的时间,并有足够的活动,将有崩溃OutOfMemoryError 。 当看着MAT的HPROF数据,我注意到所有这些操作在内存故步自封的是,他们被挟持的,因为的垃圾收集器Messenger 。 显然, Messenger实例被创建到活页夹一个长期的本地连接计数为GC根,这是保持每个“操作”对象在内存下去。

有谁知道,如果有一种方法来清除或禁用的Messenger时,“操作”结束,因此不会产生此内存泄漏? 是否有可能实现IPC的另一种方式Service以同样的方式,使多个不同的对象可以提出请求并异步得到的结果?

提前致谢!

Answer 1:

由于从戴安娜Hackborn在Android团队一些非常有益的见解,这个问题是因为远程服务过程中有没有垃圾回收它的这实际上,举行应用程序的过程人质的情况下,直到那个时候使者的实例。

这是她的答复全文:

的确,在进程发送一个信使将需要持有就可以了GREF为其他进程与之通信。 除非错误(这已经发生了,但我不知道,如果在任何发布平台的版本),当其他进程本身不再持有该基准的GREF将被释放。 当我们在谈论的Dalvik的事情“不再持有参考”一般是指“对方已经被垃圾收集Java代理对象。”

这意味着,当你跨越到另一个进程抛出一个使者(或任何的IBinder对象),Dalvik虚拟机在自己的进程可以不再管理该对象本身的内存,并且依赖于所有的远程对象释放它,直到它可以在本地释放。 这将包括在具有的IBinder任何引用,以及所有的对象。

一个常见的模式来处理这是您的IBinder /斜挎保存到你的其余对象,这将访问参考使用WeakReference的。 这使得当地的垃圾收集清理所有其他对象(这可能是相当沉重的,拿着大事情像位图和等),即使远程进程仍然对您的IBinder的参考。 如果你这样做当然,需要有别的持有这些对象的引用,直到他们需要的不再,否则垃圾收集器可以清除它们,他们不再需要

别的东西我建议是不要做你实例信使对象为你做的每一个IPC的设计。 创建一个使者,你在传递给每IPC调用。 否则,因为对方不积极,因为所有它正在创建的对象,由于这些电话都是小垃圾收集就可以产生大量的因其他进程继续持有引用正在围绕保持远程对象的。

更多信息: https://groups.google.com/d/msg/android-developers/aK2o1W2xrMU/Z0-QujnU3wUJ



Answer 2:

我不知道这是否是因为最好的办法,即使Activity是在后台,你会得到的messageService

我想你应该绑定到service并注册一个messenger ,只要服务连接到服务。 然后注销的messenger ,当你断开连接。

检查ExportVcardActivity在AOSP。 这是继东西沿着这些路线。



文章来源: Messenger to Remote Service Causing Memory Leak