I've identified serious memory leak issues in my app, managing connection to a service when an activity is quickly created/destroyed (or possibly quickly resumed/pause; at least this is what I am thinking based on testing).
I would like some help to understand what is causing this and how to fix it properly. This happens when I switch from another app to my app, and the screen orientation was different (and my activity holding the connection object is not in the foreground).
I have a bunch of activities that can't seem to be garbage collected because they seem to still be referenced by: 'LoadedApk$ServiceDispatcher$DeathMonitor' objects at the GC root.
I call bind in onResume() and unBind in onPause(). After doing some experiment, I believe the way I bind/unbind somehow the root cause of the issue. For example if i stop calling unbind, the memory leak stops but i get some ConnectionLeaked exceptions (I understand their cause). Maybe calling unbind before the connection object receives a notification is causing this issue somehow (but i don't see errors in logcat, and the service properly receives the bind/unbind calls).
I see a bunch of activities instances with the following path to GC root when i use the Eclipse Memory Analyzer (after I switch back and forth between my app and another app a few times):
MyActivity
<--mContext android.app.LoadedApk$ServiceDispatcher @ 0x41059d6
<----this$0 android.app.LoadedApk$ServiceDispatcher$DeathMonitor @ 0x4105b548 Native Stack
My source code is basically:
public class MyActivity extends Activity {
private final String TAG = "MyActivity";
public IREventService mREventService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
mREventService = IREventService.Stub.asInterface(service);
Log.e(TAG, "connected to service:"+MyActivity.this.toString()+ "connection: "+this.toString());
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "Service has unexpectedly disconnected:"+this.toString());
mREventService = null;
}
};
@Override
protected void onResume() {
super.onResume();
Intent intent = new Intent(this, EventService.class);
intent.setAction(this.toString());
Log.w(TAG, "bindService:"+this.toString());
bindService(intent,
mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onPause() {
super.onPause();
Log.w(TAG, "unbinding from service:"+this.toString());
unbindService(mConnection);
}
}
The logcats logs look like: (EventService is the service, and i am logging in onBind() and onUnBind() )
<I switched to another app>
06-17 17:27:08.965: WARN/MyActivity(5987): MyActivity onStop()
<this is where i switch back to my app>
06-17 17:27:09.415: INFO/ActivityManager(154): Config changed: { scale=1.0 imsi=310/4 loc=en_US touch=3 keys=1/1/2 nav=1/2 orien=L layout=0x10000014 uiMode=0x11 seq=169}
06-17 17:29:44.855: WARN/MyActivity(5987): MyActivity onDestroy()
06-17 17:29:44.855: INFO/TabletStatusBar(205): DISABLE_BACK: no
06-17 17:29:45.015: WARN/MyActivity(5987): MyActivity onCreate()
06-17 17:29:45.015: WARN/MyActivity(5987): MyActivity onResume()
06-17 17:29:45.015: WARN/MyActivity(5987): bindService:MyActivity@408753a0
06-17 17:29:45.015: WARN/EventService(6009): onBind() MyActivity@408753a0
06-17 17:29:45.025: WARN/MyActivity(5987): MyActivity onPause()
06-17 17:29:45.025: WARN/MyActivity(5987): unbinding from service:MyActivity@408753a0
06-17 17:29:45.025: WARN/EventService(6009): onUnBind() MyActivity@408753a0
06-17 17:29:45.025: ERROR/MyActivity(5987): connected to service:MyActivity@408753a0connection: com.yogiplay.apps.MyActivity$1@40878328
06-17 17:29:45.065: DEBUG/dalvikvm(5987): GC_FOR_ALLOC freed 32K, 3% free 31936K/32839K, paused 34ms
06-17 17:29:45.125: DEBUG/dalvikvm(5987): GC_FOR_ALLOC freed 65K, 4% free 32895K/33927K, paused 36ms
06-17 17:29:45.205: DEBUG/dalvikvm(5987): GC_FOR_ALLOC freed 803K, 5% free 33550K/35079K, paused 30ms
06-17 17:29:45.245: DEBUG/dalvikvm(5987): GC_FOR_ALLOC freed 65K, 4% free 34109K/35335K, paused 27ms
06-17 17:29:45.255: INFO/dalvikvm-heap(5987): Grow heap (frag case) to 36.976MB for 3722256-byte allocation
06-17 17:29:45.285: DEBUG/dalvikvm(5987): GC_FOR_ALLOC freed <1K, 4% free 37744K/38983K, paused 25ms
06-17 17:29:45.335: DEBUG/dalvikvm(5987): GC_CONCURRENT freed <1K, 4% free 37744K/38983K, paused 2ms+3ms
06-17 17:29:45.645: INFO/WindowManager(154): Setting rotation to 3, animFlags=1
06-17 17:29:45.665: INFO/ActivityManager(154): Config changed: { scale=1.0 imsi=310/4 loc=en_US touch=3 keys=1/1/2 nav=1/2 orien=P layout=0x10000014 uiMode=0x11 seq=170}
06-17 17:29:45.685: DEBUG/FlurryAgent(5770): Ending session
06-17 17:29:45.755: WARN/MyActivity(5987): MyActivity onStop()
06-17 17:29:45.755: WARN/MyActivity(5987): MyActivity onDestroy()
06-17 17:29:45.755: WARN/IInputConnectionWrapper(5770): showStatusIcon on inactive InputConnection
06-17 17:29:45.865: WARN/MyActivity(5987): MyActivity onCreate()
06-17 17:29:45.865: WARN/MyActivity(5987): MyActivity onResume()
06-17 17:29:45.865: WARN/MyActivity(5987): bindService:MyActivity@409e4c00
06-17 17:29:45.875: WARN/EventService(6009): onBind() MyActivity@409e4c00
06-17 17:29:45.875: WARN/MyActivity(5987): MyActivity onPause()
06-17 17:29:45.875: WARN/MyActivity(5987): unbinding from service:MyActivity@409e4c00
06-17 17:29:45.875: WARN/EventService(6009): onUnBind() MyActivity@409e4c00
06-17 17:29:45.875: ERROR/MyActivity(5987): connected to service:MyActivity@409e4c00connection: com.yogiplay.apps.MyActivity$1@409e4d20
06-17 17:29:45.925: DEBUG/dalvik开发者_开发知识库vm(5987): GC_FOR_ALLOC freed 6130K, 18% free 32743K/39815K, paused 24ms
06-17 17:29:46.005: DEBUG/dalvikvm(5987): GC_FOR_ALLOC freed 883K, 16% free 33737K/39815K, paused 23ms
06-17 17:29:46.035: DEBUG/dalvikvm(5987): GC_FOR_ALLOC freed 1K, 15% free 33970K/39815K, paused 23ms
thanks for the help.
It looks like you've run into a known pre-Android-4.0 race condition between ServiceConnection.onServiceConnected and Context.unbindService.
On pre-Android-4.0 versions of Android, if you call Context.unbindService before you have received a call to the ServiceConnection.onServiceConnected method of the bound service's service connection, the framework will leak a JNI reference to your context by way of the android.app.LoadedApk$ServiceDispatcher$DeathMonitor object.
It is very likely that you are hitting this problem -- if you look at your log, you will see that your onUnbind message is being printed before the onConnected message:
06-17 17:29:45.875: WARN/EventService(6009): onUnBind() MyActivity@409e4c00
06-17 17:29:45.875: ERROR/MyActivity(5987): connected to service:MyActivity@409e4c00connection: com.yogiplay.apps.MyActivity$1@409e4d20
This bug has been fixed for Android 4.0. Here's the commit that fixed the bug:
https://github.com/android/platform_frameworks_base/commit/5a6ef737edbf57577443ac056613afe6cb121519
For earlier versions of Android, a work-around is to delay calling unbindService until after you have received a call to ServiceConnection.onServiceConnected. For what it is worth, it is OK to call Context.unbindService from within your service connection's onServiceConnected callback method.
精彩评论