I have an application that communicates with a Service in a remote process using the Messenger interface. 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
Handlerwrapped in aMessengerused to receive the response data back from theService - When the operation executes, it wraps its
Messengerinto anIntentand callsstartService()to pass the message to the remote service - The remote service does some work based on the parameters of the
Intentand then returns the response by sending aMessageto theMessengerfor 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 snippet of how the Service is structured (it’s basically an IntentService that doesn’t shut down when the queue is empty):
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 an OutOfMemoryError. 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 the Messenger. 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.

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 another way to implement the IPC to the Service in the same fashion, so that multiple disparate objects can make a request and get a result asynchronously?
Thanks in advance!
Thanks to some very helpful insight from Dianne Hackborn on the Android team, the issue is because the remote service process has not yet Garbage Collected it’s instance of the Messenger which, in effect, held the instances in the application’s process hostage until that time.
This is the text of her reply:
More Info:
https://groups.google.com/d/msg/android-developers/aK2o1W2xrMU/Z0-QujnU3wUJ