`

Avoid memory leaks on Android

阅读更多
我们平常开发的时候,会经常遇到Out of Memorry这样的问题,我这两天在关注这方面的解决方案,下面就把一些宝贵的资料和大家一起分享一下。

1,Google I/O大会上也有这个专题Google I/O: Memory Management for Android Apps,请了Patrick developer进行详细介绍:可以通过下面的url去看其视频:http://www.youtube.com/user/googledevelopers?blend=1&ob=4#p/search/0/_CruQY55HOk

Patrick的博客:http://dubroy.com/blog/google-io-memory-management-for-android-apps/

2,a blogpost by Romain Guy:Avoid memory leaks on Android,http://www.curious-creature.org/2008/12/18/avoid-memory-leaks-on-android/

Avoid memory leaks on Android
Thursday, December 18th, 2008 at 1:57 pm, 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.

3,对Romain Guy的那篇文章进行分析和理解消化的文章:http://stackoverflow.com/questions/6567647/avoid-memory-leaks-on-androidhttp://blog.csdn.net/zhanghw0917/article/details/5659353

但后来发现了问题。就是网上很多的关于Drawable会使用setCallback保留对组件(ImageButton )的引用,而组件又会保留对activity的引用,这样activity上的计数器不会0,虚拟机也不能回收已经不用的activity对象,再次引发内存溢出。

//View.java
public void setBackgroundDrawable(Drawable d) {  
        boolean requestLayout = false;  
  
        mBackgroundResource = 0;  
  
        /* 
         * Regardless of whether we're setting a new background or not, we want 
         * to clear the previous drawable. 
         */  
        if (mBGDrawable != null) {  
            mBGDrawable.setCallback(null);  
            unscheduleDrawable(mBGDrawable);  
        }  
  
        if (d != null) {  
            Rect padding = sThreadLocal.get();  
            if (padding == null) {  
                padding = new Rect();  
                sThreadLocal.set(padding);  
            }  
            if (d.getPadding(padding)) {  
                setPadding(padding.left, padding.top, padding.right, padding.bottom);  
            }  
  
            // Compare the minimum sizes of the old Drawable and the new.  If there isn't an old or  
            // if it has a different minimum size, we should layout again  
            if (mBGDrawable == null || mBGDrawable.getMinimumHeight() != d.getMinimumHeight() ||  
                    mBGDrawable.getMinimumWidth() != d.getMinimumWidth()) {  
                requestLayout = true;  
            }  
  
            d.setCallback(this);  
            if (d.isStateful()) {  
                d.setState(getDrawableState());  
            }  
            d.setVisible(getVisibility() == VISIBLE, false);  
            mBGDrawable = d;  
  
            if ((mPrivateFlags & SKIP_DRAW) != 0) {  
                mPrivateFlags &= ~SKIP_DRAW;  
                mPrivateFlags |= ONLY_DRAWS_BACKGROUND;  
                requestLayout = true;  
            }  
        } else {  
            /* Remove the background */  
            mBGDrawable = null;  
  
            if ((mPrivateFlags & ONLY_DRAWS_BACKGROUND) != 0) {  
                /* 
                 * This view ONLY drew the background before and we're removing 
                 * the background, so now it won't draw anything 
                 * (hence we SKIP_DRAW) 
                 */  
                mPrivateFlags &= ~ONLY_DRAWS_BACKGROUND;  
                mPrivateFlags |= SKIP_DRAW;  
            }  
  
            /* 
             * When the background is set, we try to apply its padding to this 
             * View. When the background is removed, we don't touch this View's 
             * padding. This is noted in the Javadocs. Hence, we don't need to 
             * requestLayout(), the invalidate() below is sufficient. 
             */  
  
            // The old background's minimum size could have affected this  
            // View's layout, so let's requestLayout  
            requestLayout = true;  
        }  
  
        computeOpaqueFlags();  
  
        if (requestLayout) {  
            requestLayout();  
        }  
  
        mBackgroundSizeChanged = true;  
        invalidate();  
    }  



这里面的d.setCallback(this)可能会导致保留对组件的引用。当重新调用setBackgroundDrawable时,Drawable调用mBGDrawable.setCallback(null)先将原来的引用设置为null。



回到我的项目,我们在切换横竖屏时并没有重新生成activity,也没有需要销毁的activity,而且最初我们没有设置为static,那么这些成员变量的生命周期和activity是相同的,不会引发上述问题。(理论推测)我们目前的做法是在onConfigurationChanged()中增加了这么句话:

mWaterDrawable .setCallback(null)做个双保险。



那当将Drawable的变量设置为static后,应该在onDestroy中也调用mWaterDrawable .setCallback(null),保证activity可以被正常回收。

4,对Memorry查看的文章:http://www.greatereader.org/posts/developer/memory-analysis-for-android-applications_20783.htmlhttp://stackoverflow.com/questions/2298208/how-to-discover-memory-usage-of-my-application-in-android/2299813#2299813

分享到:
评论

相关推荐

    Detected memory leaks简单检测方法

    ### Detected Memory Leaks 简单检测方法 #### 背景介绍 在软件开发过程中,内存泄漏是一个常见的问题,特别是在使用C/C++等需要手动管理内存的语言进行开发时更为突出。内存泄漏不仅会导致程序占用的内存持续增长...

    Finding memory leaks发现内存的泄漏(6KB)

    这篇“Finding memory leaks发现内存的泄漏”可能是关于如何定位和解决内存泄漏的技术指南。 在C++编程中,程序员需要手动管理内存,通过`new`关键字申请内存,然后通过`delete`关键字释放内存。如果忘记释放或错误...

    论文研究-A Tool for Testing Java Memory Leaks.pdf

    标题《论文研究-A Tool for Testing Java Memory Leaks.pdf》和描述《一个测试Java内存泄漏的工具,党万春,钱巨,虽然Java语言有自带的垃圾回收机制,Java程序仍然存在由无效引用带来的内存泄漏。而内存泄漏会导致...

    How catch memory leaks with very little effort (7KB)

    标题 "How catch memory leaks with very little effort (7KB)" 提示了这是一个关于如何轻松检测内存泄漏的技术分享。内存泄漏是编程中的一个常见问题,尤其是在C++等不自动管理内存的语言中,程序员需要手动分配和...

    MemoryLeaks:内存泄漏的常见情况和使用MLeaksFinder的检测

    平常我们都会用 Instrument 的 Leaks / Allocations 或其他一些开源库进行内存泄露的排查,但是检查过程非常繁琐,而且不清晰,最主要的是Abandoned memory不会被检测出来。 Leaks 从苹果的开发者文档里可以看到,一...

    Java memory leaks

    ### Java内存泄漏详解 #### 摘要 本文旨在探讨如何在IBM WebSphere Application Server (WAS) 3.5、4.01 和 5.0 版本上诊断和定位 Java 内存泄漏问题,包括分布式环境和 z/OS 环境。文章将介绍一种通用的方法论,...

    Eliminating Memory Leaks in Symbian OS C++ Projects

    ### 消除Symbian OS C++项目中的内存泄漏 #### 概述 在Symbian OS中,内存管理是一项至关重要的任务,特别是在C++项目中。本文将深入探讨如何在Symbian OS环境中检测、避免内存泄漏,并提供具体方法来解决这些问题...

    c++test完整版教材为中国软件测试人员工作

    5. **Avoid leaks of memory allocated for 'glob_t' structure [BD-LEAKS-GLOB-1]**:检测并避免'glob_t'结构体内存泄漏。 6. **Avoid leaks of memory allocated for 'hostent' structure [BD-LEAKS-HOSTENT-1]**...

    Finding memory leaks

    在压缩包中的文件,如`memory_leaks.shtml.htm`,可能包含了更详细的内存泄漏检测和解决方法。`VC Empire.htm`可能涉及到在Visual C++环境中如何处理内存泄漏的问题。阅读这些文件可以获取更多实用技巧和案例分析。 ...

    Android代码-loading button

    Progress Button Android Android Button that morphs into a loading progress bar. Fully customizable in the XML ...Avoid Memory Leaks Be Creative Bugs and feedback Credits Installation implement

    checkmemoryleak

    内存泄漏是程序运行过程中的常见问题,特别是在C和C++这样的低级编程语言中,由于程序员需要手动管理内存,不正确的内存分配和释放可能导致内存泄漏。`__wrap_malloc`是一种调试技术,用于检测由`malloc()`函数引起...

    BREW Memory Leak Checker 20120219

    This program is a tool that help you positioning memory leak locations of an QUALCOMM BREW application on simulator. It can give the call stack of memory leaks (including the locations that MALLOC/...

    Devart_UniDAC_6.3.13_for_XE8

    Bug with memory leaks on application finalization when Pooling is enabled is fixed Bug on processing timestamp(6) field is fixed Bug with cutting the STATUS field for SHOW ENGINE statement is fixed ...

    memory leakge & initialization & invalid pointer

    - **Solution**: Follow a clear policy on who is responsible for freeing memory. Typically, the function that allocates memory should also be responsible for freeing it. **1.4 Temporary Variables** - ...

    Objective C Memory Management Essentials(PACKT,2015)

    You will begin with a basic understanding of memory management, and why memory leaks occur in an application, moving on to autorelease pools and object creation/storage to get an idea of how memory ...

    Android代码-Console

    An Android console view, which allows you to log text using static calls, to easily debug your application, whilst avoiding memory leaks. Usage Include Console anywhere in your layout: then ...

    Detecting Passive Content Leaks and Pollution in Android Applications

    文章《Detecting Passive Content Leaks and Pollution in Android Applications》主要探讨了在Android应用程序中两个关键的安全漏洞,这些漏洞都与Android的一个不受保护的组件ContentProvider有关。由于缺少必要的...

    BREW Memory Leak Checker 20120219 (with src)

    This program is a tool that help you positioning memory leak locations of an QUALCOMM BREW application on simulator. It can give the call stack of memory leaks (including the locations that MALLOC/...

    Devart_UniDAC_6.3.13_for_DX10.1

    Bug with memory leaks on application finalization when Pooling is enabled is fixed Bug on processing timestamp(6) field is fixed Bug with cutting the STATUS field for SHOW ENGINE statement is fixed ...

Global site tag (gtag.js) - Google Analytics