避免Android内存泄露

转载 2012年03月29日 20:19:26

Android的应用被限制为最多占用16m的内存,至少在T-Mobile G1上是这样的(当然现在已经有几百兆的内存可以用了——译者注)。它包括电话本身占用的和开发者可以使用的两部分。即使你没有占用全部内存的打算,你也应该尽量少的使用内存,以免别的应用在运行的时候关闭你的应用。Android能在内存中保持的应用越多,用户在切换应用的时候就越快。作为我的一项工作,我仔细研究了Android应用的内存泄露问题,大多数情况下它们是由同一个错误引起的,那就是对一个上下文(Context)保持了长时间的引用。

在Android中,上下文(Context)被用作很多操作中,但是大部分是载入和访问资源。这就是所有的widget都会在它们的构造函数中接受一个上下文(Context)参数。在一个合格的Android应用中,你通常能够用到两种上下文(Context):活动(Activity)和应用(Application)。活动(Activity)通常被传递给需要上下文(Context)参数的类或者方法:

[java] view plaincopy
  1. <ol><li class="li1 odd"><div class="de1">@Override</div></li><li class="li1 even"><div class="de1"><span class="kw1">protected</span> <span class="kw4">void</span> onCreate<span class="br0">(</span>Bundle state<span class="br0">)</span> <span class="br0">{</span></div></li><li class="li1 odd"><div class="de1">  <span class="kw1">super</span>.<span class="me1">onCreate</span><span class="br0">(</span>state<span class="br0">)</span><span class="sy0">;</span></div></li><li class="li1 even"><div class="de1"> </div></li><li class="li1 odd"><div class="de1">  TextView label <span class="sy0">=</span> <span class="kw1">new</span> TextView<span class="br0">(</span><span class="kw1">this</span><span class="br0">)</span><span class="sy0">;</span></div></li><li class="li1 even"><div class="de1">  label.<span class="me1">setText</span><span class="br0">(</span><span class="st0">"Leaks are bad"</span><span class="br0">)</span><span class="sy0">;</span></div></li><li class="li1 odd"><div class="de1"> </div></li><li class="li1 even"><div class="de1">  setContentView<span class="br0">(</span>label<span class="br0">)</span><span class="sy0">;</span></div></li><li class="li1 odd"><div class="de1"><span class="br0">}</span></div></li></ol>  

这就意味着那个View有一个对整个活动(Activity)的引用并且对这个活动(Activity)中保持的所有对象有保持了引用;通常它们包括整个View的层次和它的所有资源。因此,如果你“泄露”了上下文(Context)(这里“泄露”的意思是你保持了一个引用并且组织GC收集它),你将造成大量的内存泄露。如果你不够小心的话,“泄露”一整个活动(Activity)是件非常简单的事情。

当屏幕的方向改变时系统会默认的销毁当前的活动(Activity)并且创建一个新的并且保持了它的状态。这样的结果就是Android会从资源中重新载入应用的UI。现在想象一下,你写了一个应用,有一个非常大的位图,并且你并不想在每次旋转时都重新载入。保留它并且每次旋转不重新加载的最简单的办法就是把它保存在一个静态字段上:

[java] view plaincopy
  1. <ol><li class="li1 odd"><div class="de1"><span class="kw1">private</span> <span class="kw1">static</span> Drawable sBackground<span class="sy0">;</span></div></li><li class="li1 even"><div class="de1"> </div></li><li class="li1 odd"><div class="de1">@Override</div></li><li class="li1 even"><div class="de1"><span class="kw1">protected</span> <span class="kw4">void</span> onCreate<span class="br0">(</span>Bundle state<span class="br0">)</span> <span class="br0">{</span></div></li><li class="li1 odd"><div class="de1">  <span class="kw1">super</span>.<span class="me1">onCreate</span><span class="br0">(</span>state<span class="br0">)</span><span class="sy0">;</span></div></li><li class="li1 even"><div class="de1"> </div></li><li class="li1 odd"><div class="de1">  TextView label <span class="sy0">=</span> <span class="kw1">new</span> TextView<span class="br0">(</span><span class="kw1">this</span><span class="br0">)</span><span class="sy0">;</span></div></li><li class="li1 even"><div class="de1">  label.<span class="me1">setText</span><span class="br0">(</span><span class="st0">"Leaks are bad"</span><span class="br0">)</span><span class="sy0">;</span></div></li><li class="li1 odd"><div class="de1"> </div></li><li class="li1 even"><div class="de1">  <span class="kw1">if</span> <span class="br0">(</span>sBackground <span class="sy0">==</span> <span class="kw2">null</span><span class="br0">)</span> <span class="br0">{</span></div></li><li class="li1 odd"><div class="de1">    sBackground <span class="sy0">=</span> getDrawable<span class="br0">(</span>R.<span class="me1">drawable</span>.<span class="me1">large_bitmap</span><span class="br0">)</span><span class="sy0">;</span></div></li><li class="li1 even"><div class="de1">  <span class="br0">}</span></div></li><li class="li1 odd"><div class="de1">  label.<span class="me1">setBackgroundDrawable</span><span class="br0">(</span>sBackground<span class="br0">)</span><span class="sy0">;</span></div></li><li class="li1 even"><div class="de1"> </div></li><li class="li1 odd"><div class="de1">  setContentView<span class="br0">(</span>label<span class="br0">)</span><span class="sy0">;</span></div></li><li class="li1 even"><div class="de1"><span class="br0">}</span></div></li></ol>  

这段代码非常快,同时也错的够离谱。它泄露了当第一次屏幕角度改变时创建的第一个活动(Activity)。当一个Drawable被附加到一个View,这个View被设置为drawable的一个回调。在上面的代码片断中,这意味着这个Drawable对TextView有一个引用,同时这个TextView对Activity(Context对象)保持着引用,同时这个Activity对很多对象又有引用(这个多少还要看你的代码了)。

这个例子是造成Context泄露的最简单的一个原因,你可以看一下我们在主屏幕源码(查看unbindDrawables()方法)中是通过在Activity销毁时设置保存过的Drawable的回调为空来解决这个问题的。更为有趣的是,你可以创建一个context泄露的链,当然这非常的糟糕。它们可以让你飞快的用光所有的内存。

有两种简单的方法可以避免与context相关的内存泄露。最明显的一个就是避免在context的自身的范围外使用它。上面的例子展示了在类内部的一个静态的引用和它们对外部类的间接引用是非常危险的。第二个解决方案就是使用Application Context。这个context会伴随你的应用而存在,并且不依赖Activity的的生命周期。如果你计划保持一个需要context的长生命周期的对象,请记得考虑Application对象。你可以非常方便的通过调用Context.getApplicationContext() 或者 Activity.getApplication()获取它。

总之,为了避免涉及到context的内存泄露,请记住如下几点:

  1. 不要对一个Activity Context保持长生命周期的引用(一个对Activity的引用应该与Activity自身的生命周期相同)
  2. 尝试使用应用上下文(context-application)代替活动上下文(context-activity)
  3. 如果你不能控制它们的生命周期,在活动(Activity)中避免使用不是静态的内部类,使用静态类并且使用弱引用到活动(Activity)的内部。对于这个问题的解决方法是使用静态的内部类与一个弱引用(WeakReference)的外部类。就像ViewRoot和它的W内部类那么实现的。
  4. 垃圾回收器对于内存泄露来说并不是百分百保险的。

原文地址:Avoiding memory leaks

Android中内存泄露与如何有效避免OOM总结

Android系统为每个应用程序分配的内存有限,当一个应用中产生的内存泄漏比较多时,就难免会导致应用所需要的内存超过这个系统分配的内存限额,这就造成了内存溢出而导致应用Crash。内存泄露的危害:只有...
  • gjnm820
  • gjnm820
  • 2016年06月03日 15:09
  • 4299

Android内存优化方案和内存泄露检测分析方法

常见内存泄露及优化方案1、单例错误写法:public class AppSettings { private static AppSettings sInstance; private ...
  • iromkoear
  • iromkoear
  • 2017年04月26日 20:54
  • 472

Android中使用Thread造成内存泄露的分析和解决

Thread 内存泄露线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控。看一下下面是否存在问题 public class ThreadActivity e...
  • jdsjlzx
  • jdsjlzx
  • 2016年05月13日 00:08
  • 5481

Android之AsyncTask异步任务——防止内存泄露

Android的异步任务有很多实现方法,最常见的如Handler、Thread、AsyncTask;还有一些开源项目也可以做到异步任务和线程间通信等功能,例如:EventBus、RxAndroid等,...
  • zsf442553199
  • zsf442553199
  • 2016年07月25日 11:20
  • 554

Java 内存泄露以及避免方法

Java代码   内存泄露:      是指在程序运行过程中会不断的分配内存空间,那些不再使用的内存空间应该即时回收它们,从而保证可以保证系统可以再次使用这些内存。如果存在无用的内存没...
  • jay1110C
  • jay1110C
  • 2016年08月26日 09:11
  • 312

android-不容易发现的内存泄漏

一、Handler引起的泄漏假如程序如下方式书写:Handler mHandler = new Handler() { @Override public void han...
  • birdfly12345
  • birdfly12345
  • 2016年09月04日 21:48
  • 461

webview 内存泄露解决办法

最近开始使用leakCanary为app进行内存泄露的检测 遇到了webview.mContext导致activity内存泄露 (不过在android 6.0的机子上没有遇到这样的问题) ...
  • EdwardWayne
  • EdwardWayne
  • 2016年03月24日 17:39
  • 1771

Android App 内存泄露之Thread

Thread 内存泄露 线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控。 1.看一下下面是否存在问题 public class ThreadActivity...
  • zhuanglonghai
  • zhuanglonghai
  • 2014年07月17日 17:15
  • 6827

android--弱引用来预防内存泄露

是否我们经常写的代码无缘无故的提示我们内存泄露,或者是程序跑着跑着就挂了,其实这和我们写的代码风格有关,以及写的代码不规范,...
  • liuxiangjia
  • liuxiangjia
  • 2015年04月11日 16:05
  • 519

详解java内存泄露和如何避免内存泄漏

源地址:http://www.xttblog.com/?p=518 一直以来java都占据着语言排行榜的头把交椅。这是与java的设计密不可分的,其中最令大家喜欢的不是面向对象,而是垃圾回收机制。你...
  • xidiancyp
  • xidiancyp
  • 2016年05月15日 18:10
  • 4247
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:避免Android内存泄露
举报原因:
原因补充:

(最多只允许输入30个字)