内存泄漏(二)

来源:互联网 发布:上海大剧院 座位 知乎 编辑:程序博客网 时间:2024/05/22 00:44

引言

首先我们先来写一个程序模拟一下内存泄漏,方法很多,上一篇安卓面试系列–内存泄漏(一)中我们讲了很多造成内存泄漏的原因,不知道大家还记不记得,不记得的话记得多看几遍,然后自己默写处出来。这里还是带大家简单过一遍吧,首先是单例模式的静态关键字和创建单例时需要传入的context,第二个是非静态内部类创建静态实例,第三个是Handler,第四个AsyncTask和线程,最后一个是一些资源未关闭,比如bitmap,cursor,ContentObserver。

public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    public void onclick(View view) {        Intent intent = new Intent(this,SecondActivity.class);        startActivity(intent);    }}
public class SecondActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_second);        new Thread(new Runnable() {            @Override            public void run() {                try {                    Thread.sleep(60000000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }).start();    }}
  • 下面我们从MainActivity跳到SecondActivity 然后从SecondActivity返回MainActivity,连续这样5次 ,最终返回MainActivity,按照常理来说,我们从SecondActivity返回MainActivity之后 SecondActivity就该被销毁回收,可实际可能并不是这样;
  • SecondActivity有个子线程进行长时间的耗时操作,导致SecondActivity回收不了,重复几次后会导致SecondActivity有多个实例,导致内存泄漏。

查找内存泄漏

1、直接使用Android Studio的Monitor Memory查找

  • 首先 在手机上运行程序,打开AS的 Minotor 界面 查看Memory 图像
    这里写图片描述

  • 点击 小卡车图标(图中1位置图标) 可以触发一次 GC
    这里写图片描述

  • 点击 图中2位置图标可以查看hprof文件
    这里写图片描述

  • 左边是 内存中的对象,在里面找 Activity 看存不存在我们希望已经回收的Activity 如果 出现我们期望已经回收的Activity,单击 就会在右边显示它的总的个数,点击右边的某个,可以显示 它的GC Roots的树关系图 ,查看关系图就可以找出发生内存泄漏的位置

2、利用MAT工具查找(重点)

  • 首先打开AS中的Android Device Monitor工具 具体位置如下图:
    这里写图片描述

  • 打开后会出现如下的界面
    这里写图片描述

  • 先选中你要检测的应用的包名,然后点击下图画圈的地方,会在程序包名后标记一个图标
    这里写图片描述

  • 接下来要做的就是操作我们的app 来回跳转5次。之后点击下图的图标 就可导出hprof文件进行分析了
    这里写图片描述

  • 导出文件如下图所示:
    这里写图片描述

    -如果没有这个工具的,我这里提供这个网址供大家下载:MAT工具下载地址
    这里写图片描述

  • 界面如下图所示:
    这里写图片描述

  • 打开我们先前导出的hprof文件 ,不出意外会报下面的错误
    这里写图片描述

  • 这是因为MAT是用来分析Java程序的hprof文件的 与Android导出的hprof有一定的格式区别,因此我们需要把导出的hprof文件转换一下,sdk中提供给我们转换的工具 hprof-conv.exe 在下图的位置

这里写图片描述

  • 接下来我们cd到这个路径下执行这个命令转换我们的hprof文件即可,如下图
    这里写图片描述

其中 hprof-conv 命令 这样使用
hprof-conv 源文件 输出文件
比如 hprof-conv E:\aaa.hprof E:\output.hprof
就是 把aaa.hprof 转换为output.hprof输出 output.hprof就是我们转换之后的文件,图中 mat2.hprof就是我们转换完的文件。
接下来 我们用MAT工具打开转换之后的mat2.hprof文件即可 ,打开后不报错 如下图所示:
这里写图片描述

之后我们就可以查看当前内存中存在的对象了,由于我们内存泄漏一般发生在Activity中,因此只需要查找Activity即可。
点击下图中标记的QQL图标 输入 select * from instanceof android.app.Activity
类似于 SQL语句 查找 Activity相关的信息 点击 红色叹号执行后 如下图所示:
这里写图片描述

  • 接下来 我们就可以看到下面过滤到的Activity信息了如上图所示, 其中内存中还存在 6个SecondActivity实例,但是我们是想要全部退出的,这表明出现了内存泄漏;
  • 其中 有 Shallow size 和 Retained Size两个属性

    • Shallow Size
      • 对象自身占用的内存大小,不包括它引用的对象。针对非数组类型的对象,它的大小就是对象与它所有的成员变量大小的总和。当然这里面还会包括一些java语言特性的数据存储单元。针对数组类型的对象,它的大小是数组元素对象的大小总和。
    • Retained Size
      • Retained Size=当前对象大小+当前对象可直接或间接引用到的对象的大小总和。(间接引用的含义:A->B->C, C就是间接引用),不过,释放的时候还要排除被GC Roots直接或间接引用的对象。他们暂时不会被被当做Garbage。
  • 接下来 右击一个SecondActivity
    这里写图片描述

  • 选择 with all references,打开如下图所示的页面
    这里写图片描述
  • this0Activitythis0又被 target引用 target是一个线程,原因找到了,内存泄漏的原因 就是 Activity被 内部类引用 而内部类又被线程使用 因此无法释放,我们转到这个类的代码处查看

总结

好了,以上就是关于安卓中最常用的两种查找内存泄漏的工具,具体流程的话不用死记硬背,面试的时候就说会使用Android Studio的Monitor Memory和MAT工具查找内存泄漏就可以了,具体里面的内容问的也不多,毕竟只是一个工具而已,主要还是要在敲代码的过程中避免内存泄漏,真正有了内存泄漏,能想起来有这两个东西就可以了。

原创粉丝点击