简体   繁体   中英

Understanding Memory Leaks in an Android application

I'm rather new to Java programming, with much C++ experience, and I'm reading about how references can lead to memory leaks in Android applications. This explanation has me stumped. At "Lesson #2" it says:

The point is, the Activity doesn't know that the lifespan of SomeObject will end when the instance of the Activity ends. If that object remains in memory, it will hold that Activity in memory as well [...].

As I see it (probably wrong; please correct me), when the activity ends, SomeObject is destroyed (assuming no other reference to it exists). The activity references SomeObject , not the other way round. I don't see why anything is leaked here, let alone the entire activity.

It has to do with the fact that they're creating an anonymous class for the EventListener.

public void onResume() {
    super.onResume();

    SomeObject object = new SomeObject();

    object.setSuccessListener(new EventListener<Boolean>() {
        public void onEvent(Boolean response) {
            Log.d(TAG_NAME, "Valid response? "+response);
        }
    });

    SomeObjectManager.getSingleton().addObject(object);
}
  • It's explicitly stated that this is being done in the Activity's onResume().

  • Anonymous classes (as well as non-static inner classes) have an implicit reference to their surrounding class. So in this case, the EventListener has a reference to the Activity itself.

  • Therefore, the SomeObject has a reference to the Activity because it has a reference to the Anonymous class that implements EventListener.

Here's the text before the quote you referenced in your question:

For instance, in the example above: we've attached a reference to our Activity instance to some object, presumably persistent, and in a manager somewhere. The point is...

So the SomeObjectManager class, which does not go away when the activity gets destroyed, holds a reference to the SomeObject, and the SomeObject holds a reference to the EventListener, which in turn references the Activity.

So when you say:

As I see it (probably wrong; please correct me), when the activity ends, SomeObject is destroyed (assuming no other reference to it exists). The activity references SomeObject, not the other way round.

The flaw in that logic is that SomeObject DOES reference the activity, through EventListener.

Does that help?

Here is the answer: How Does this Escape?

If you can do that, then run this in debugger and set breakpoint on:

Log.d(TAG_NAME, "Valid response? "+response);

now check out which members does the 'this' instance have. In particular you are interested in this$0 .

So the call is important:

objectFromBefore.setSuccessListener(null);

see what happens before that:

object.setSuccessListener(new EventListener<Boolean>() {
    public void onEvent(Boolean response) {
        Log.d(TAG_NAME, "Valid response? "+response);
    }
});

SomeObjectManager.getSingleton().addObject(object);

now the chain is:

SomeObjectManager -> singleton -> listofObjects -> object

and for object:

object->successListeners->anonymous EventListener->this$

this$ here is your outer instance which is activity.

Unfortunately, it is VERY easy to leak an Activity. Some Android devs I know are happily (and sometimes purposely) oblivious to this fact. Kudos to you for caring enough to know you want to learn more to avoid this situation.

Some common ways you can leak an Activity

1) setting/adding/registering your Activity as a listener or observer and forgetting to set(null)/remove/unregister the Activity.

2) Handing a reference to your Activity off to anything that holds onto it using a static reference and not clearing the static reference. (Singletons are an example of this)

3) Using non-static inner classes. (anonymous inner class Handlers are an example of this)

Some things you can do to avoid these types of leaks

1) Remember to set(null)/remove/unregister your Activity

2) As part of your software design, bake into your components that deal with your Activity the concept of the Activity lifecycle.

3) Make inner classes static, and where you need a reference to the outer class, use WeakReferences

4) Use the application context where possible instead of your Activity context.

Some common circumstances under which your Activity will be destroyed and recreated

1) If you allow orientation changes in your app, the Activity will be destroyed and recreated when switching from portrait to landscape and vice versa.

2) Exiting out of your application using the device's back button will cause the current Activity to be destroyed (even though the app itself will remain running.)

3) Although more rare, Android can kill your Activity at will in order to free resources and keep the device responsive for the user.

You should familiarize yourself with Android Studio's Device Monitor, the hprof-conv tool, and the Eclipse Memory Analyzer Tool. The easiest way I've found to test my app to see if it is leaking an Activity is to memory profile it in Device Monitor while I go in to the app, exit out of it using the device back button, and return to the app by clicking on its icon in the app tray or going to recents. Repeat this a few times and then create a memory dump file. Run the hprof-conv tool on this dump file to turn it into a format MAT can understand, and then open it in MAT.

There are many tutorials out there on getting a memory profile and using MAT. Just do a web search.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM