Android 内存性能优化,面向Android开发者的复习指南

==============================================================================

在上面的Heap Dump中我们提到了一个 .hprof文件,在我们分析内存的时候,我们有时候无法很确定内存泄漏的地方,我们会通过 Heap Dump来生成一个 疑似发生内存泄漏时所产生的 堆存储文件,它就是 .hprof文件。

MAT就是用来分析 .hprof文件的。

MAT的全称为 Memory Analysis Tool,是对内存进行详细分析的工具,它是Eclipse的插件,AS需要单独下载。

下载地址在 MAT官网下载网址

我们打开来运行一下刚刚生成的 .hprof文件,可能报以下的错误:

…Unknown HPROF Version (JAVA PROFILE 1.0.3)…

这个意思的是我们由Android虚拟机转出来的.hprof文件和 MAT所分析的 .hprof(用JVM)的格式不一样,我们需要转化文件格式。

我们先通过命令行,进入到 该.hprof所在目录,比如我有一个叫做 android.hprof由Android导出来的 ,然后执行下面命令:

hprof-conv android.hprof mat.hprof

就会在该目下出现一个 mat.hprof的文件,我们就可以用这个文件导入啦:

在这里插入图片描述

下面我们来举个实例,来介绍怎么使用MAT。

4.1 生成一个hprof文件


我们先准备一份内存泄漏的代码出来:

public class MainActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

LeakThread leakThread = new LeakThread();

leakThread.start();

}

class LeakThread extends Thread {

@Override

public void run() {

try {

Thread.sleep(60 * 60 * 1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

上面做了一个 非静态内部类LeakThread持有外部 MainActivity的引用,LeakThread中做了耗时操作,导致MainActivity无法被释放。

接着我们运行它,产生一个 .hprof文件。然后我们打开Profiler,横竖屏切换,然后导出一个 .hprof文件,然后通过 hprof-cov转换它,最后拿转换出来的文件导入到 MAT中。

在这里插入图片描述

4.2 MAT分析hprof文件


用MAT打开hprof文件后,MAT就会产生一个报告,分为两个标签页,分别是 OverviewLeak Suspects

在Leak Suspects中给出了MAT认为可能出现内存泄漏问题的地方,在上图中共给出了2个内存泄漏的猜想,通过单击每个猜想的Details可以看到更深入的分析情况。如果内存泄漏不是特别明显,通过Leak Suspects很难发现内存泄漏的位置。

打开Overview标签页,首先看到的是一个饼状图:

在这里插入图片描述

它主要用来显示内存的消耗,饼状图的彩色区域代表被分配的内存,灰色区域表示空闲内存区。

再往下看,能看到饼状图下面有 Actions

在这里插入图片描述

其中分析内存泄漏最常用的就是 HistogramDominator Tree。下面我来介绍它怎么使用。

4.3 Dominator Tree


Dominator Tree的意思是支配树,它更善于去分析对象的引用关系,如下图所示:

在这里插入图片描述

左边主要是Class Name类名,右边是三列之前都看到过:

  • Shallow Heap

对象自身占用的内存大小,不包括它引用的对象。如果是数组类型的对象,它的大小由数组元素的类型和数组长度决定。如果是非数组类型的对象,它的大小由其成员变量的数量和类型决定。

  • Retained Heap

一个对象的Retained Set包含对象所占内存的总大小。

换句话说,Retained Heap就是当前对象被GC后,从Heap上总共能释放掉都少的内存。

Retained Set指的是这个对象本身和它持有引用的对象以及这些对象的Retained Set所占内存大小的总和,引用树官方的图解如下图:

在这里插入图片描述

从上图可以看出 E的Retained Set为E和G,C的Retained Set为C、D、E、F、G、H。

MAT所定义的支配树就是从上图中的树演化而来。

在引用树中,如果一条到Y的路径必然会经过X,则称为X支配Y。X直接支配Y则指的是在所有支配Y的对象中,X是Y最近的一个对象。支配树反映的就是这种直接支配关系,在支配树中,父节点直接支配子节点。

下图就是官方提供的一个从引用树到支配树的转换示意图。

在这里插入图片描述

上图中 由于C直接到达D和E,所以C是D和E的父节点。而到达H有两条路径 DF和EG,但是这两条是互斥的,对于互相来说都是不必要的,抽象一层,相当于C能直接到达H,所以C是H的父节点~

通过支配树,我们就能很容易的分析一个Retained Set所以 假如 E被回收,E、G的内存会被释放,而H不会。因为可能F还引用着H

所以通过MAT提供的Dominator Tree,我们可以很清晰地得到一个对象的直接支配对象,如果直接支配对象中出现了不该有的对象,就说明发生了内存泄漏。在Dominator Tree的顶部Regex可以输入过滤条件,如果查找的是Activity内存泄漏,可以在Regex中输入Activity的名称,比如这个例子可以输入MainActivity:

在这里插入图片描述

可以看到MainActivity出现了14个实例,而项目中MainActivity是不能有那么多实例的,基本可以判定发生了内存泄漏。

具体内存泄漏的原因,可以查看GC引用链,在MainActivity选项上单击右键->Path To GC Roots。

在这里插入图片描述

可以看到根据引用类型会有多种选项,比如 with all references就是包含所有的引用,这里我们选择exclude all phantom/weak/soft etc. references,因为这个选项排除了虚引用,弱引用和软引用,这些引用一般是可以被回收的,这是MAT就会给出MainActivity的GC引用链,如下图所示:

在这里插入图片描述

可以看到,引用MainActivity的就是LeakThread,this$0的含义就是内部类自动保留一个指向所在外部类的引用,而这个外部类就是MainActivity,这将会导致MainActivity无法被GC。

4.4 Histogram


Histogram与Dominator Tree不同的是,Dominator Tree是从对象实例的角度进行分析,注重引用关系分析,而Histogram则从类的角度进行分析,注重量的分析。

在这里插入图片描述

这里有四列,Obejcts表代表对象实例的个数。我们同样可以在这里输入过滤条件 MainActivity。

在这里插入图片描述

可以看到MainActivity和LeakThread的示实例各有6个,基本上可以断定发生了内存泄漏,具体内存泄漏的原因,同样可以查看GC引用链。在MainActivity选项上单击鼠标右键->Merge Shortest Paths to GC roots,并在选项中选择 exclude all phantom/weak/soft ets. references,Path To GC Roots的信息如下图所示:

在这里插入图片描述

Histogram是从类的角度进行分析,而Path To GC Roots是用来分析单个对象的,因此在Histogram中无法使用Path To GC Roots查询,可以使用 Merge Shortest Paths to GC Roots查询,他表示从GC roots到一个或一组对象的公共路径。得出的结果和之前是相同的,引用MainActivity的是Leak Thread。

4.5 OQL


OQL全称为Object Query Language,类似于SQL语句的查询语言,能够用来查询当前内存中满足指定条件的所有对象。

它的查询语句的基本格式如下:

SELECT * FROM [ INSTANCEOF ] <class_name> [ EHERE ]

我们输入 select * from instanceof android.app.Activity并按F5,将当前内存中所有的Activity都显示出来:

在这里插入图片描述

当然如果Activity太多,也可以输入具体完整的名称,然后就可以右键他们去查看GC引用链。

4.6 对比hprof文件


因为之前举得例子很简单,可以通过上面的方法可以找到内存泄漏的原因,但是复杂的情况就需要通过对比hprof文件来进行分析了。

我们再生成一个hprof文件然后用MAT打开。对比步骤为:

(1)将每个hprof文件的 Domiator Tree或者Histogram添加到 Compare Basket

在这里插入图片描述

(2)在Compare Basket中单击红色叹号生成Compared Tables:

在这里插入图片描述

可以看到第一个文件有LeakThread和MainActivity各有6个,而第二个文件的MainActivity实例只有1个,对比之下,说明第一个文件发生了内存泄漏。

到这里我们就介绍完了MAT的常用功能,当然MAT还有其他的功能,需要我们在之后使用中去积累了。

5.LeakCanary(内存泄漏分析神器)

=======================================================================================

如果使用MAT来分析内存问题,会有一些难度,并且效率也不是很高,对一个内存泄漏的问题,可能要多排查和对比。

为了能够迅速地发现内存泄漏,Square公司基于MAT开源了LeakCanary。Github-leackcanary

5.1 导入LeakCanary


首先要导入leakcanary:

dependencies {

//这里是2.2版本,和2.0之前的版本玩法很不一样

debugImplementation ‘com.squareup.leakcanary:leakcanary-android:2.2’

}

如果在LeakCanary2.0之前的版本还要去App中加入下面的代码

@Override

public void onCreate() {

super.onCreate();

if (LeakCanary.isInAnalyzerProcess(this)) {

//如果当前的进程是用来给LeakCanary进行堆分析的则返回,否则会执行LeakCanary的install方法

return;

}

LeakCanary.install(this);

}

这样我们在打开App的时候开始检测。LeakCanary2.0前后版本变化较大,在LeakCanary2.0版本之前,需要在Application中初始化LeakCanary,在Activity、Fragment等地方要我们手动的在 onDestroy()中取监控GC情况。

而2.0版本之后连初始化都不用了,它会自己运行在主进程中。如果检测到某个Activity有内存泄漏,LeakCanary就会提示。

5.2 LeakCanary举例


我们还是拿之前的内存泄漏的代码举例子:

public class MainActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

LeakThread leakThread = new LeakThread();

leakThread.start();

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

分享读者

作者2013年java转到Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

被人面试过,也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括腾讯,以及字节跳动,阿里,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

腾讯T3架构师学习专题资料

如果你觉得自己学习效率低,缺乏正确的指导,可以点击加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧

群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括腾讯,以及字节跳动,阿里,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

[外链图片转存中…(img-z473ms43-1711295356076)]

如果你觉得自己学习效率低,缺乏正确的指导,可以点击加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧

群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

  • 16
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值