文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

详解Android性能优化之内存泄漏

2022-06-06 05:14

关注

综述

内存泄漏(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中两个符号的含义。

ShallowHeap:对象自身占用的内存大小,不包括他引用的对象
RetainedHeap:对象自身占用的内存大小并且加上它直接或者间接引用对象的大小

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 Studio 3.0上分析内存泄漏的原因Android中LeakCanary检测内存泄漏的方法Android 5.1 WebView内存泄漏问题及快速解决方法Android内存泄漏排查利器LeakCanaryAndroid常见的几种内存泄漏小结Android 内存溢出和内存泄漏的问题5个Android开发中比较常见的内存泄漏问题及解决办法Android性能优化之利用Rxlifecycle解决RxJava内存泄漏详解详解Android内存泄漏检测与MAT使用Android开发:浅谈MVP模式应用与内存泄漏问题解决Android Studio+MAT实战内存泄漏


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯