Avoid memory leaks on Android

本文深入探讨了Android应用中常见的内存泄漏问题,特别是由长时间保留的Activity引用引起的内存泄漏。文章提供了避免此类问题的两个简单方法:避免在Activity生命周期之外保留Context引用,并使用Application Context替代Activity Context。

OK,整个阅读的关键点,要注意的是 JAVA内存泄露一个很重要的问题:循环引用导致的无法释放,即A->B->A这样的引用关系,而且这个问题并不容易直接看代码可以解决。


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 ContextActivity 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 (theContext) 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 theApplication 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. Markus Kohler  says:

    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. Markus Kohler  says:

    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. Alexey Volovoy  says:

    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. [...] 并且Romain给了一篇介绍类似内存泄露的blog. http://www.curious-creature.org/2008/12/18/avoid-memory-leaks-on-android/ 在这篇blog中, 可以看到更多的Android实现细节. [...]

  11. Markus Kohler  says:

    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

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

  13. 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??

  14. 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.

  15. 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.

  16. justin crow  says:

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

    Thanks very much for the clarification.

  17. 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.

  18. [...] though. If the object you pass is for some reason tied to the Activity/Context, you will leak all the views and resources of the activity. This means you should never pass a View, a Drawable, [...]

  19. [...] though. If the object you pass is for some reason tied to the Activity/Context, you will leak all the views and resources of the activity. This means you should never pass a View, a Drawable, [...]

  20. [...] though. If the object you pass is for some reason tied to the Activity/Context, you will leak all the views and resources of the activity. This means you should never pass a View, a Drawable, [...]

  21. Filipe Abrantes  says:

    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

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

  23. Xpaнитeль  says:

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

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

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

  26. 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 !

  27. 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

  28. 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

  29. 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.

  30. 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.

  31. Arboleda  says:

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

  32. Silknet  says:

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

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

  34. 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.

  35. web cam girls  says:

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

  36. [...] hours of searching I found a tiny explanation in Romain Guy’s blog. There are two easy ways to avoid context-related memory leaks. The most obvious one is to avoid [...]

  37. glguro  says:

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

  38. 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.

  39. 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.

  40. 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.


下载方式:https://pan.quark.cn/s/c9b9b647468b ### 初级JSP程序设计教程核心内容解析#### 一、JSP基础概述JSP(JavaServer Pages)是由Sun Microsystems公司创建的一种动态网页技术规范,主要应用于构建动态网站及Web应用。JSP技术使得开发者能够将动态数据与静态HTML文档整合,从而实现网页内容的灵活性和可变性。##### JSP的显著特性:1. **动态与静态内容的分离**:JSP技术支持将动态数据(例如数据库查询结果、实时时间等)嵌入到静态HTML文档中。这种设计方法增强了网页的适应性和可维护性。2. **易用性**:开发者可以利用常规的HTML编辑工具来编写静态部分,并通过简化的标签技术将动态内容集成到页面中。3. **跨平台兼容性**:基于Java平台的JSP具有优良的跨操作系统运行能力,能够在多种不同的系统环境中稳定工作。4. **强大的后台支持**:JSP能够通过JavaBean组件访问后端数据库及其他资源,以实现复杂的数据处理逻辑。5. **执行效率高**:JSP页面在初次被请求时会被转换为Servlet,随后的请求可以直接执行编译后的Servlet代码,从而提升了服务响应的效率。#### 二、JSP指令的运用JSP指令用于设定整个JSP页面的行为规范。这些指令通常放置在页面的顶部,向JSP容器提供处理页面的相关指导信息。##### 主要的指令类型:1. **Page指令**: - **语法结构**:`<%@ page attribute="value" %>` - **功能**:定义整个JSP页面的运行特性,如设定页面编码格式、错误处理机制等。 - **实例**: ...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值