views:

71

answers:

1

I have an application that uses a Service and some list activities. When the activities are opened, I can see the heap usage increase in DDMS, when the activities are closed, the heap usage decreases slightly. The service is still running in the background at this point. If the activity is started again by re-running the application and the closed, the heap usage increases again then decreases, but never returns to the original level before the activity was first opened. If it repeatedly (10-15 times) open the activity then close the activity, the heap size (both MB and # Objects) balloons!

I'd expect ListActivity's onDestroy to take care of itself when it gets destroyed. What am I missing with this? Am I using ListActivity incorrectly?

A test app similar to my real code is below. Create a new android application, add this to the manifest:

<service android:name="LeakTestService"/>

and these java files:

LeakTestActivity.java
-------------
package LeakTest.Test;

import java.util.ArrayList;
import java.util.HashMap;

import android.app.Activity;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.SimpleAdapter;

public class LeakActivity extends ListActivity {
    ArrayList> _Data=new ArrayList>();
    ArrayAdapter _Adapter;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Intent svc = new Intent(this.getApplicationContext(), LeakTestService.class);
        startService(svc);

        // the problem happens with both SimpleAdapter and ArrayAdapter
        //_Adapter = new SimpleAdapter(this.getApplicationContext(), _Data, android.R.layout.two_line_list_item, new String[] { "line1","line2" }, new int[] { android.R.id.text1, android.R.id.text2 });
        _Adapter = new ArrayAdapter(this.getApplicationContext(), android.R.layout.simple_list_item_1, new String[] {"data1","data2"} );

        // if this line is removed, the heap usage never balloons if you repeatedly open+close it
        getListView().setAdapter(_Adapter);
    }

    @Override
    public void onDestroy() {
        _Adapter=null; // this line doesn't help
        getListView().setAdapter(null); // neither does this line
        super.onDestroy();
    }
}



LeakTestService.java
--------
package LeakTest.Test;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;

public class LeakTestService extends Service {
    @Override
    public void onStart(Intent intent, int startId) {
        Toast.makeText(getBaseContext(), "Service onStart", Toast.LENGTH_SHORT).show();
    }

    @Override public void onDestroy() {
        Toast.makeText(getBaseContext(), "Service onDestroy", Toast.LENGTH_SHORT).show();
        }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }
}
+1  A: 

If it repeatedly (10-15 times) open the activity then close the activity, the heap size (both MB and # Objects) balloons!

Have you run the garbage collector? There's a button for that in DDMS.

Once you have run the garbage collector and it seems to be coming up with nothing to collect (as logged in LogCat), you can always dump the heap and inspect it with MAT or jhat.

Am I using ListActivity incorrectly?

Delete all occurrences of getApplicationContext() in your activity and replace them with this. The activity is a Context, and you want to be using the Activity in those places.

Similarly, I don't know why you are calling getBaseContext() in the Service, rather than just using this, since Service too is a Context.

CommonsWare
Agree that you should explicitly run GC a couple of times before declaring that memory has been leaked. There's nothing obvious in the code you submitted that should cause problems.
beekeeper
The reason it says there is nothing to collect is because an object somewhere is still holding a reference to the activity even when it closes (i think), which means that everytime the activity reopens it will still hold a reference to the previous one somewhere. It doesnt matter whether i use this, getBaseContext or getApplicationContext... the same ballooning heap still happens. I read elsewhere that using a reference to an Activity was a bad way of 'leaking the context' if it is stored in a static variable, so have avoided it (i'm aware i dont use any static variables here)
kebab
@kebab: As I wrote, "Once you have run the garbage collector and it seems to be coming up with nothing to collect (as logged in LogCat), you can always dump the heap and inspect it with MAT or jhat." A heap analysis will tell you what is still holding onto the activity, if anything.
CommonsWare
I'll try the heap analysis when I get a moment. Thanks
kebab
@CommonsWare: So I've run MAT. I'm not entirely sure how to use it, but I've come across this: in the 'dominator_tree', I can see lots of android.widget.ListView classes, one for each of the times that the application is opened and closed. So they are definitely hanging around when they shouldn't be. I would have thought that ListActivity would take care of them. Should I explicitly add code to remove them?
kebab
@kebab: Well, I have to admit, I've never needed to crack open MAT. But, based upon what I see in some screeshots, you should be able to discern the path to the GC roots via some right-mouse options. See http://www.eclipse.org/mat/about/dominator_tree.png and http://www.eclipse.org/mat/about/path_2_gc_roots.png. If I had to guess, they all have a common GC root, and once we know what that is, we'll be a step closer to helping you figure out why these are sticking around.
CommonsWare
Ok, so here's what I've found. It looks like the ListView's themselves are GC roots (I think thats what the orange dot means). I have no idea what's going on here :( http://imgur.com/P1DRN.png http://imgur.com/6G5BC.png
kebab
@kebab: I'm getting 503 errors on those image URLs in your last comment.
CommonsWare
@CommonsWare: Try these: http://tinypic.com/r/nl74nn/7 http://tinypic.com/r/2lc0dop/7
kebab
@kebab: I cannot explain what you're seeing there. Looking at the `ListView` source code, it has no mutable static data members. When you did the path-to-GC-roots image, did you choose "exclude weak/soft references"?
CommonsWare
@CommonsWare: I did 'with all reference', but I have just done it again with 'exclude weak/soft references' and it looks the same; one single row.
kebab
@kebab: Well, at this point, I am stumped. Sorry!
CommonsWare