It is true that sending a messenger across processes will require holding a
GREF on it for the other process to communicate with it. Barring bugs
(which have happened but I am not sure if in any released platform
versions), the GREF will be released when the other process itself no
longer holds a reference on this.  When we are talking about things in
Dalvik "no longer holds a reference" generally means "the other side has
garbage collected the Java proxy object."

What this means is that when you throw a Messenger (or any IBinder object)
across to another process, the Dalvik VM in your own process can no longer
manage the memory of that object itself and is dependent on all remote
objects releasing it until it can be released locally.  And this will
include all objects that the IBinder has any references to as well.

A common pattern to deal with this is to use a WeakReference in your
IBinder/Messenger that holds the references to the rest of your objects
that it will access.  This allows your local garbage collector to clean up
all of those other objects (which may be quite heavy, holding big things
like bitmaps and such) even though a remote process still has a reference
on your IBinder.  Of course if you do this, there needs to be something
else holding a reference on these other objects until they are no longer
needed, or else the garbage collector could clean them up *before* they are
no longer needed.

Something else I would recommend is to not do a design where you
instantiate Messenger objects for each IPC you do.  Create one Messenger
that you pass in to each IPC call.  Otherwise you can generate a lot of
remoted objects that are being kept around due to other processes
continuing to hold references because the other side is not aggressively
garbage collecting since all the objects it is creating due to these calls
are small.

On Thu, Aug 30, 2012 at 2:42 PM, Dave Smith <dasmith1...@gmail.com> wrote:

> I have an application that communicates with a Service in a remote
> process using the Messengerinterface. Here is the basic architecture of
> how things are set up:
>
>    - The application generates several "Operation" objects that require
>    access to the service.
>    - Each "Operation" contains a Handler wrapped in a Messenger used as a
>    callback receive the response data back from the Service
>    - When the operation executes, it wraps its Messenger into an Intent and
>    calls startService() to pass the message to the remote service
>    - The remote service does some work based on the parameters of the
>    Intent and then returns the response by sending a Message to the
>    Messenger for that operation.
>
> Here is the basic code present in the operation:
>
> 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);
> }
>
> And a basic snippet of how the service is structured:
>
> 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);
>     }
> }
>
> This all works fantastically well. I can send tons of operations from
> several different applications to the same service and they all process and
> send their response to just the right place. However...
>
> I noticed that if the application ran long enough and with enough activity
> it would crash with anOutOfMemoryError. Upon looking at the HPROF data in
> MAT, I noticed that all these operations where staying in memory, and they
> were held hostage from the Garbage Collector because of theMessenger.
> Apparently, the Messenger instance is creating a long-term native
> connection to Binder that counts as a GC Root, which is keeping each
> "Operation" object in memory indefinitely.
>
> [image: MAT Trace Example]
>
> Does anyone know if there is a way to clear or disable the Messenger when
> the "Operation" is over so it doesn't create this memory leak?
>
> Is there perhaps a better way to implement the IPC to the Servicein the
> same fashion, so that multiple disparate objects can make a request and get
> a result asynchronously?  I'm not convinced that just switching to using a
> bound service implementation will solve this issue as I would will need the
> Messenger to process the callback.
>
> --
> You received this message because you are subscribed to the Google
> Groups "Android Developers" group.
> To post to this group, send email to android-developers@googlegroups.com
> To unsubscribe from this group, send email to
> android-developers+unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/android-developers?hl=en




-- 
Dianne Hackborn
Android framework engineer
hack...@android.com

Note: please don't send private questions to me, as I don't have time to
provide private support, and so won't reply to such e-mails.  All such
questions should be posted on public forums, where I and others can see and
answer them.

-- 
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

Reply via email to