摘要:综述 内存泄漏(memory leak)是指因为疏忽或错误形成程序未能释放已经再也不使用的内存。那么在Android中,当一个对象持有Activity的引用,若是该对象不能被系统回收,那么当这个Activity再也不使用时,这个Activity也不会被系统回收,那这么以来便出现了内存泄漏的状况。
综述php
内存泄漏(memory leak)是指因为疏忽或错误形成程序未能释放已经再也不使用的内存。那么在Android中,当一个对象持有Activity的引用,若是该对象不能被系统回收,那么当这个Activity再也不使用时,这个Activity也不会被系统回收,那这么以来便出现了内存泄漏的状况。在应用中内出现一次两次的内存泄漏获取不会出现什么影响,可是在应用长时间使用之后,如果存在大量的Activity没法被GC回收的话,最终会致使OOM的出现。那么咱们在这就来分析一下致使内存泄漏的常见因素而且如何去检测内存泄漏。java
致使内存泄漏的常见因素android
情景一:静态Activity和View面试
静态变量Activity和View会致使内存泄漏,在下面这段代码中对Activity的Context和TextView设置为静态对象,从而产生内存泄漏。性能优化
import android.content.Context; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private static Context context; private static TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); context = this; textView = new TextView(this); } } 复制代码
情景二:Thread,匿名类,内部类bash
在下面这段代码中存在一个非静态的匿名类对象Thread,会隐式持有一个外部类的引用LeakActivity,从而致使内存泄漏。同理,如果这个Thread做为LeakActivity的内部类而不是匿名内部类,他一样会持有外部类的引用而致使内存泄漏。在这里只须要将为Thread匿名类定义成静态的内部类便可(静态的内部类不会持有外部类的一个隐式引用)。架构
public class LeakActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_leak); leakFun(); } private void leakFun(){ new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } 复制代码
情景三:动画app
在属性动画中有一类无限循环动画,若是在Activity中播放这类动画而且在onDestroy中去中止动画,那么这个动画将会一直播放下去,这时候Activity会被View所持有,从而致使Activity没法被释放。解决此类问题则是须要早Activity中onDestroy去去调用objectAnimator.cancel()来中止动画。框架
public class LeakActivity extends AppCompatActivity { private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_leak); textView = (TextView)findViewById(R.id.text_view); ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(textView,"rotation",0,360); objectAnimator.setRepeatCount(ValueAnimator.INFINITE); objectAnimator.start(); } } 复制代码
情景四:Handlereclipse
对于Handler的内存泄漏在(Android的消息机制——Handler的工做过程)[http://blog.csdn.net/ljd2038/article/details/50889754]这篇文章中已经详细介绍,就不在赘述。
情景五:第三方库使用不当
对于EventBus,RxJava等一些第三开源框架的使用,如果在Activity销毁以前没有进行解除订阅将会致使内存泄漏。
使用MAT检测内存泄漏
对于常见的内存泄露进行介绍完之后,在这里再看一下使用MAT(Memory Analysis Tool)来检测内存泄露。MAT的下载地址为:http://www.eclipse.org/mat/downloads.php。
下面来看一段会致使内存泄露的错误代码。
public class LeakActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_leak); EventBus.getDefault().register(this); } @Subscribe public void subscriber(String s){ } } 复制代码
在上面这段代码中有会致使内存泄漏,缘由是EventBus没有解除注册。下面就以这段代码为例来看一下如何分析内存泄漏。
打开AndroidStudio中的Monitors能够看到以下界面。
在这里能够看到在应用刚启动的时候,所占用的内存为15M,而后咱们如今开始操做APP,反复进入退出LeakActicity。点击上如中的GC按钮。这时候咱们在看一下内存使用状况。
在这里咱们能够看到,内存一直在持续增长,已经达到33M,而且没法被GC所回收。因此咱们能够判断,这时候必然出现内存泄漏的情形。那么如今再点击Dump Java Heap按钮,在captures窗口看到生成得hprof文件。但这时候所生成的hprof文件不是标准格式的,咱们须要经过SDK所提供的工具hprof-conv进行转化,该工具在SDK的platform-tools目录下。执行命令以下:
复制代码
固然在AndroidStudio中能够省去这一步,能够直接导出标准格式的hprof文件。
这时候能够经过MAT工具来打开导出的hprof文件。打开界面以下图所示:
在MAT中咱们最经常使用的就是Histogram和Dominator Tree,他们分别对应上图中的A和B按钮。Histogram能够看出内存中不一样类型的buffer的数量和占用内存的大小,而Dominator Tree则是把内存中的对象按照从大到小的顺序进行排序,而且能够分析对象之间的引用关系。在这里再来介绍一下MAT中两个符号的含义。
Histogram
因为在Android中通常内存泄漏大多出如今Acivity中,这时候能够点击Histogram按钮,并搜索Activity。
在这里能够看出LeakActivity存在69个对象,基本上能够判定存在内存泄漏的情形,这时候即可以经过查看GC对象的引用链来进行分析。点击鼠标右键选择Merge Shortest paths to GC Roots并选择exclude weak/soft references来排除弱引用和软引用。
在排除软引用和弱引用之后以下图所示:
在这里能够看出因为EventBus致使的LeakActivity内存泄漏。
在Histogram中还能够查看一个对象包含了那些对象的引用。例如,如今要查看LeakActivity所包含的引用,能够点击鼠标右键,选择list objects中的with incoming reference。而with outcoming reference表示选中对象持有那些对象的引用。
Dominator Tree
如今咱们点击这时候能够点击Dominator Tree按钮,并搜索Activity。能够看到以下图所示:
在这里能够看到存在大量的LeakActivity。而后点击鼠标右键选择Path To GC Roots->exclude weak/soft references来排除弱引用和软引用。
以后能够看到以下结果,依然是EventBus致使的内存泄漏:
总结
内存泄漏每每被咱们所忽略,可是当大量的内存泄漏之后致使OOM。它所形成的影响也是不容小觑的。固然除了上述内存泄漏的分析觉得咱们还能够经过LeakCanary来分析内存泄漏。对于LeakCanary的使用在这里就不在进行详细介绍。