综述
内存泄漏(memory leak)是指由于疏忽或错误造成程序未能释放已经不再使用的内存。那么在Android中,当一个对象持有Activity的引用,如果该对象不能被系统回收,那么当这个Activity不再使用时,这个Activity也不会被系统回收,那这么以来便出现了内存泄漏的情况。在应用中内出现一次两次的内存泄漏获取不会出现什么影响,但是在应用长时间使用以后,若是存在大量的Activity无法被GC回收的话,最终会导致OOM的出现。那么我们在这就来分析一下导致内存泄漏的常见因素并且如何去检测内存泄漏。
导致内存泄漏的常见因素
情景一:静态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,匿名类,内部类
在下面这段代码中存在一个非静态的匿名类对象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(); } } }); } }
情景三:动画
在属性动画中有一类无限循环动画,如果在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(); } }
情景四:Handler
对于Handler的内存泄漏在(Android的消息机制——Handler的工作过程)
情景五:第三方库使用不当
对于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目录下。执行命令如下:
hprof-conv XXX.hprof converted-dump.hprof
当然在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的使用在这里就不在进行详细介绍。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
本文向大家介绍Android性能优化之利用Rxlifecycle解决RxJava内存泄漏详解,包括了Android性能优化之利用Rxlifecycle解决RxJava内存泄漏详解的使用技巧和注意事项,需要的朋友参考一下 前言: 其实RxJava引起的内存泄漏是我无意中发现了,本来是想了解Retrofit与RxJava相结合中是如何通过适配器模式解决的,结果却发现了RxJava是会引起内存泄漏的,所
本文向大家介绍Android Bitmap详解及Bitmap的内存优化,包括了Android Bitmap详解及Bitmap的内存优化的使用技巧和注意事项,需要的朋友参考一下 Android Bitmap详解及Bitmap的内存优化 一、Bitmap: Bitmap是Android系统中的图像处理的最重要类之一。用它可以获取图像文件信息,进行图像剪切、旋转、缩放等操作,并可以指定格式保存图像文件。
我写了一段代码,让字母在我写的时候出现并飞行。这个问题消耗了大量的内存。 我已经优化了一点 在侦听器中共享对象并更新其参数。 每次打印新字母时调用 gc 但是它仍然使用大量的内存,所以有什么想法来降低它的内存利用率吗? 提前致谢。 操作系统:Arch Linux 64位平台:英特尔i7-第三代,8 GB内存IDE : Intellij JDK : 1.8.0_102
本文向大家介绍总结Android App内存优化之图片优化,包括了总结Android App内存优化之图片优化的使用技巧和注意事项,需要的朋友参考一下 前言 在Android设备内存动不动就上G的情况下,的确没有必要去太在意APP对Android系统内存的消耗,但在实际工作中我做的是教育类的小学APP,APP中的按钮、背景、动画变换基本上全是图片,在2K屏上(分辨率2048*1536)一张背景图片
本文向大家介绍mysql优化之like和=性能详析,包括了mysql优化之like和=性能详析的使用技巧和注意事项,需要的朋友参考一下 引言 那使用过数据库的人大部分都知道,like和=号在功能上的相同点和不同点,那我在这里简单的总结下: 1,不同点:like可以用作模糊查询,而'='不支持此功能;如下面的例子,查询info表中字段id第一个字母为1的数据: 2,相同点:like和"="都可以进行
本文向大家介绍mysql服务性能优化—my.cnf_my.ini配置说明详解(16G内存),包括了mysql服务性能优化—my.cnf_my.ini配置说明详解(16G内存)的使用技巧和注意事项,需要的朋友参考一下 此配置是老男孩生产线上使用的配置,在培训的时候,他给的,我在这里,对各参数添加了中文说明 这配置已经优化的不错了,如果你的mysql没有什么特殊情况的话,可以直接使用该配置参数 MYS