Avoid memory leaks on Android

Android applications are, at least on the T-Mobile G1, limited to 16 MB of heap. It’s both a lot of memory for a phone and yet very little for what some developers want to achieve. Even if you do not plan on using all of this memory, you should use as little as possible to let other applications run without getting them killed. The more applications Android can keep in memory, the faster it will be for the user to switch between his apps. As part of my job, I ran into memory leaks issues in Android applications and they are most of the time due to the same mistake: keeping a long-lived reference to a Context.

On Android, a Context is used for many operations but mostly to load and access resources. This is why all the widgets receive a Context parameter in their constructor. In a regular Android application, you usually have two kinds of Context, Activity and Application. It’s usually the first one that the developer passes to classes and methods that need a Context:

@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);
  
  TextView label = new TextView(this);
  label.setText("Leaks are bad");
  
  setContentView(label);
}

This means that views have a reference to the entire activity and therefore to anything your activity is holding onto; usually the entire View hierarchy and all its resources. Therefore, if you leak the Context (“leak” meaning you keep a reference to it thus preventing the GC from collecting it), you leak a lot of memory. Leaking an entire activity can be really easy if you’re not careful.

When the screen orientation changes the system will, by default, destroy the current activity and create a new one while preserving its state. In doing so, Android will reload the application’s UI from the resources. Now imagine you wrote an application with a large bitmap that you don’t want to load on every rotation. The easiest way to keep it around and not having to reload it on every rotation is to keep in a static field:

private static Drawable sBackground;

@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);
  
  TextView label = new TextView(this);
  label.setText("Leaks are bad");
  
  if (sBackground == null) {
    sBackground = getDrawable(R.drawable.large_bitmap);
  }
  label.setBackgroundDrawable(sBackground);
  
  setContentView(label);
}

This code is very fast and also very wrong; it leaks the first activity created upon the first screen orientation change. When a Drawable is attached to a view, the view is set as a callback on the drawable. In the code snippet above, this means the drawable has a reference to the TextView which itself has a reference to the activity (the Context) which in turns has references to pretty much anything (depending on your code.)

This example is one of the simplest cases of leaking the Context and you can see how we worked around it in the Home screen’s source code (look for the unbindDrawables() method) by setting the stored drawables’ callbacks to null when the activity is destroyed. Interestingly enough, there are cases where you can create a chain of leaked contexts. I can’t remember an exact case right now, but I have fixed a couple of those, and they are bad. They make you run out of memory rather quickly.

There are two easy ways to avoid context-related memory leaks. The most obvious one is to avoid escaping the context outside of its own scope. The example above showed the case of a static reference but inner classes and their implicit reference to the outer class can be equally dangerous. The second solution is to use the Application context. This context will live as long as your application is alive and does not depend on the activities life cycle. If you plan on keeping long-lived objects that need a context, remember the application object. You can obtain it easily by calling Context.getApplicationContext() or Activity.getApplication().

In summary, to avoid context-related memory leaks, remember the following:

  • Do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the activity itself)
  • Try using the context-application instead of a context-activity
  • Avoid non-static inner classes in an activity if you don’t control their life cycle, use a static inner class and make a weak reference to the activity inside

And remember that a garbage collector is not an insurance against memory leaks. Last but not least, we try to make such leaks harder to make happen whenever we can.

41 Responses to “Avoid memory leaks on Android”

  1. Interesting post!
    Please consider support in dalvik for writing heap dumps that can be analyzed with http://www.eclipse.org/mat!
    This would make finding these kind of memory issues a simple exercise for everyone.

  2. Romain Guy says:

    Hi Markus,

    Dalvik can already generate heap dumps, but they cannot be read by Java heap dumps analyzers like mat, hat or JProfiler. We have a tool to analyze the heap dumps but we haven’t been able to release it yet. However, it looks like we’re working on something: http://android.git.kernel.org/?p=platform/dalvik.git;a=tree;f=hit;h=4fc699bd9ad3c447819015ee871554f7b5eea838;hb=cupcake

  3. Hi Romain,

    Thanks a lot for the link.
    Yes I know that heap dumps are already possible, but that for license reasons the parser could not be released.

    Great to hear there’s some progress.

    http://www.eclipse.org/mat can be extended for other heap dump formats. IBM already did that for their non .hprof format.

    If you need a demo of MAT to convince you that dalvik support in MAT would be a good thing to have, please let me know :)

    Regards,
    Markus

  4. Thanks a lot. I might have those too, and i assume many people too. Going back to code and revisit all my Drawables hope they not all static. If Drawable just a member field and not static – it’s fine right?
    Regards Alex.

  5. Romain Guy says:

    An instance variable will not cause any problem indeed.

  6. Mariano Kamp says:

    Hey,

    thanks for the post. Very interesting.

    The topic of the contexts is a bit annoying though I have to say. Or I still don’t get it.

    If I just want to have some component to be able to read from some small bit of persistent state, e.g. from the filesystem (as I my reusable component cannot participate in onCreate() etc.) , I need to keep a context around. Which is a shame, as I probably don’t know which activity/broadcast receiver will first need those information, but as the API is so asymmetric (no application obtainable from a broadcast receiver), that is not possible in a sane way.

    And btw. making the component a service that should run all the time, just to have a context available, doesn’t sound all that nice either.

    Cheers,
    Mariano

  7. Romain Guy says:

    In BroadcastReceiver.onReceive() you get passed a Context. That Context lets you in turn access the application context (see Context.getApplicationContext()). Just pass the application context to your component and that’s it.

  8. Mariano Kamp says:

    Romain, thanks for your quick reply. That works, my bad.

    Merci,
    Mariano

  9. Jose Luis says:

    Hi Romain,

    thanks for this post! I knew for your other posts in the developers group that leaking the Context was very bad but now I understand it better.

    Anyway, I think something is not right with GC in Dalvik. Try a very simple thing, an activity with just an ImageView that loads a large image (3.1 Mpx to use the same resolution as the G1). Rotate the screen a couple of times and you will get an OOM exception.

    Why the memory is not garbage collected once the activity is destroyed?

    Maybe this is not the best place to ask a question, but thanks anyway! ;)

    Jose Luis.

  10. FYI,
    The Eclipse Memory Analyzer will be able to load Dalvik dumps soon:
    http://kohlerm.blogspot.com/2009/01/first-androiddalvik-heap-dump-loaded.html
    Regards,
    Markus

  11. На свете есть много вещей, насчет которых разумный человек мог бы пожелать остаться в неведении.

  12. justin crow says:

    Thanks for the good work Romain – I’m a fan I have to say.

    I wasn’t sure if I misunderstood the memory leak problem in your static example. Surely when the setBackgroundDrawable() is called, the Drawable has setCallback() invoked, which will overwrite the current callback, therefore no memory leak?

    Clearly I’m missing something??

  13. Romain Guy says:

    Justin,

    If you always set it on a View, then it’s not an issue (well, except that you kept a hold onto the previous Context for longer than needed.) It can become very problematic for an application in which you use the drawables in a ListView for instance: as long as the drawable is not re-bound to a View, you keep the old Context and all the resources and Views. It’s much safer to not rely on that at all.

  14. Romain Guy says:

    There’s also the situation of when your Activity is destroyed but not recreated (it can happen for several reasons.) If the process is still there, your static fields also are and you are leaking the Activity until it is recreated.

  15. justin crow says:

    Ah yes, I see; I wasn’t thinking outside of the box.

    Thanks very much for the clarification.

  16. Fahad says:

    Thanks for this but I must say a profiling tool is desperately needed. No proper profiling can be done without good support for a GUI.

  17. Indeed a very interesting post. Recently I had a very hard time troubleshooting a memory leak on my Android app. In the end it turned out that my xml layout included a WebView component that, even if not used, was preventing the memory from being g-collected after screen rotations/app restart… is this a bug of the current implementation, or is there something specific that one needs to do when using WebViews.

    cheers

  18. Извините, как можно добавить свой материал на сайт?

  19. Интересно стало, а есть кто-то, кто не совсем согласен с автором ? :)

  20. Что-то у меня в Firefox дизайн вашего сайта расползается…

  21. I enjoy your blog. Keep on the good work, I will subscribe. How do youthink of the new Nokia N97?

  22. Piwaï says:

    Great!

    I have an applications with “beans” that are created once, and used in all activities of the application. I already have a kind of “container” that keeps the bean references, but when those beans have dependencies on a “Context” (injected in their constructor), it was kind of tricky to have them use the right Context.

    I ended up creating a CurrentContextProxy that extends Context and delegates all its calls to a real Context that is an instance variable of CurrentContextProxy. This CurrentContextProxy is injected in the beans.

    Whenever an Activity comes to the front (onResume()), it updates the CurrentContextProxy with itself.

    Althought this was better than recreating all beans for each activity, I will now use an Application context, much better :-).

    Thanks !

  23. Ramesh says:

    Hi Guys,

    This Ramesh, I have one problem in my application “Low memory No more process” I have one activity. this activity change the views corresponding action. after some my application hanged and exit automatically. last one week i struggle with this problem. I don’t know what is the problem. I don’t kept static Context variable. Please help me any body knows the solution.

    Advance thanks
    Ramesh

  24. Dimitar Dimitrov says:

    Hi, Ramesh,

    As you may know, long living references of Context aren’t the only source of memory leak problems. I suggest you take a look at this video: http://www.youtube.com/watch?v=Dgnx0E7m1GQ

    It won’t give you answer right away, but it will show you how to find your problem with the SDK tools (which are great, I must say). Learn to use TraceView, HierarchyViewer and AllocationTracker; debugging will become much more fun and programming – much easier.

    P.S. One question about the aforementioned getApplicationContext() and getApplication() methods – after reading the blog entry, I had the impression that they were static in Android 1.0/1.1, but now, in Cupcake, they are regular instance methods, am I right?

    Regards,
    Dimitar

  25. Richard G. Meng says:

    Hi

    Maybe I am wrong, but I am a little confused about the memory leak in the sample of this article, because, the setBackgroundDrawable reset the drawable’s callback after the activity reloaded.

    label.setBackgroundDrawable(sBackground);

    And you can check the source of setBackgroundDrawable(…),

    public void setBackgroundDrawable(Drawable d) {

    if (d != null) {

    d.setCallback(this);

    }

    }

    This is SDK 1.6’s code, maybe it’s updated after this post.

    -M.G.

  26. Renato Grottesi says:

    Hi Romain,

    does the 16 MB heap limitation also applies to OpenGL ES textures or SoundPool sound effects?
    Since they are managed by native code, is it correct to affirm that there is no problem in using more than 16 MB of RAM in app/game resources/assets?
    If it is possible right now, it is guarantee to be possible in the future?

    Thank you in advance for your reply,
    Renato.

  27. Arboleda says:

    Молодец, а я только недавно кому-то из блоггеров комментировал что «задолбали с постами на тему почему я перестал публиковать интересно почитать»)) Почему-почему, лень им стало, вот и перестали))

  28. Silknet says:

    P thanks to your ideas , iТd adore to adhere to your weblog as usually as i can.possess a good day

  29. Занимательно тут у Вас. ) Надо будет еще заскочить.

  30. Eneko says:

    Are these considerations also applicable to static Bitmaps used, for instance, in ImageView.setImageBitmap, or only to drawables?

    The fact is that my application uses very frequently some icons that are requested via Web Service. I am currently storing these images in a static Hashtable (using the URL of the image as its key and the Bitmap as the value) and using them in some Views, but I am afraid I am leaking memory this way.

    Thank you in advance.

  31. I love what I find here. I’ll come back. Thanks!

  32. glguro says:

    The framework is suck,if they use a weakreference for mCallback then those leak will be never happend.

  33. Just would like to say this blog is incredibly good. I always like to study some thing new about this since I have the similar blog in my Region on this subject so this help´s me a lot. I did a research on a theme and found a excellent variety of blogs but absolutely nothing like this.Thanks for sharing so much inside your blog.

  34. Linan Wang says:

    what if:
    … there is a static method: Application.getCurrentApplication() ?!
    … methods/constructors are designed clearer: don’t ask for Context if what really want is Application ?!
    … do house cleaning in Activity.onStop(), clean up all such setCallback(null) things?
    … mention the setCallback issue in the documentation?
    … use weakreferences when necessary?

    I have to say, no matter how much i like google, comparing with iOS, Android is just a third rate product. No taste in UI, nor in programming.

  35. Barb says:

    Hello all from Barb Stevens even as carcinoma of the lungs is on the increase Adultfriends. Those short conversations you can only have with a try mortal and/or individual of the word adultfriends.