Binder Callbacks, Hostage References, and Garbage Collection

154 views
Skip to first unread message

Dave Smith

unread,
Jun 19, 2014, 12:01:03 AM6/19/14
to android-...@googlegroups.com
I have put together a Binder-based (AIDL) service that follows the same callback pattern as the LocationListener in LocationManager/LocationManagerService.  By this, I mean that we register client callbacks with the service by wrapping the local listener object in a "transport" (i.e. a oneway AIDL service instance that owns the local listener) and forwarding the transport to the service via register() & unregister() methods.

The problem this implementation creates is that, even after unregistering the callback on the service side (i.e. removing any references to that instance), until the service process runs a garbage collection on its side, the transport object on the client side (and the listener that it owns) cannot be garbage collected in the client process.  Since often the local listener may be an Activity or larger object, we can't have this.  It is easy enough to work around in or own code (nulling out the local reference, etc. as part of an unregister request), but it got me thinking...

How does LocationManager + LocationManagerService not suffer from the same issue?  Is there code inside of system_server that triggers GC at just the right time to avoid these issues, or is there something special about the ListenerTransport implementation used there that I may have missed in my implementation that contributes to its ability to cleanly exit memory?

Thanks in advance,
Dave Smith, PE
Double Encore, Inc.
@devunwired

Wrythe Limited

unread,
Jun 19, 2014, 1:33:02 AM6/19/14
to android-...@googlegroups.com

One would think that you can look at that source and work out how its dealing with that issue, or not.

--
You received this message because you are subscribed to the Google Groups "android-platform" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-platfo...@googlegroups.com.
To post to this group, send email to android-...@googlegroups.com.
Visit this group at http://groups.google.com/group/android-platform.
For more options, visit https://groups.google.com/d/optout.

Dave Smith

unread,
Jun 19, 2014, 1:39:01 AM6/19/14
to android-...@googlegroups.com
One would think, and so far as I can tell there is nothing obvious done in the service to handle this issue explicitly, but obviously application developers don't have to concern themselves with this so I must be missing something.  If you have any direct insights into a part of the source I may have missed, I'd appreciate it.  I've poured over the following elements, and I certainly don't see it, if it exists:

Dave Smith

unread,
Jun 24, 2014, 11:15:25 AM6/24/14
to android-...@googlegroups.com
Welp, it turns out my original assumption was false (that the location services on the device don't suffer from the same leak problem).  There is an unresolved bug (https://code.google.com/p/android/issues/detail?id=15170) noting the same issue, and it's very easy to reproduce with a simple Activity:

public class MyActivity extends Activity implements LocationListener {

    private static final String TAG = "LocationLeaker";

    LocationManager mLocationManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);

        mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mLocationManager.removeUpdates(this);
    }

    @Override
    public void onLocationChanged(Location location) {
        Log.i(TAG, "LocationChanged");
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        Log.i(TAG, "StatusChanged");
    }

    @Override
    public void onProviderEnabled(String provider) {
        Log.i(TAG, "ProviderEnabled");
    }

    @Override
    public void onProviderDisabled(String provider) {
        Log.i(TAG, "ProviderDisabled");
    }
}

Taking a heap dump even after the activity is destroyed will show the activity/listener still being held hostage from the GC by the binder's Native Stack reference.
Turns out, this is just a bad pattern to model a service after.
Reply all
Reply to author
Forward
0 new messages