概述
1.adb shell && Memory Usage
以通过命令 adb shell dumpsys meminfo [package name] 来将指定 package name 的内存信息打印出来,这种模式可以非常直观地看到 Activity 未释放导致的内存泄漏:
或者也可以通过 Android studio 的 Memory Usage 功能进行查看,最后的结果是一样的:
2.利用finalise方法
上面介绍finalise方法时有说过:当GC准备回收一个Java Object(所有Java对象都是Object的子类)的时候,GC会调用这个Object的finalize方法。也就意味着如果某个对象比如activity泄漏的话,那么在退出这个activity时,GC不会调用这个activity的finalize方法,我们可以利用这一点,重写activity的finalise方法,在里面打印一段日志,如下所示:
我们balabala打开很多activity,再返回到主页,通过IDE手动触发几次GC操作,如果这时某个activity没有打印finalise方法里的日志时,说明这个activity就发生内存泄漏了。怎么样?是不是很简单粗暴!
需要注意一点的是,由于重写了finalize方法的对象要第二次GC才能会真正被GC回收,这也是一种泄漏,所以检查完内存泄漏后,别忘了删除重写的finalise方法。
3.Allocation Tracker
Android studio 还自带一个 Allocation Tracker 工具,功能和 DDMS 中的基本差不多,这个工具可以监控一段时间之内的内存分配:
在内存图中点击途中标红的部分,启动追踪,再次点击就是停止追踪,随后自动生成一个 .alloc 文件,这个文件就记录了这次追踪到的所有数据,然后会在右上角打开一个数据面板:
4.Android Memory Monitor
Memory Monitor 是 Android Studio 自带的一个监控内存使用状态的工具,入口如下所示:
// 待补充图片
在 Android Monitor 点开之后 logcat 的右侧就是 Monitor 工具,其中可以检测内存、CPU、网络等内容,我们这里只用到了 Memory Monitor 功能,点击红色箭头所指的区域,就会 dump 此时此刻的 Memory 信息,并且生成一个 .hprof 文件,dump 完成之后会自动打开这个文件的显示界面,如果没有打开,可以通过点击最左侧的 Capture 界面或者 Tool Window 里面的 Capture 进入 dump 的 .hprof 文件列表:
首先左上角的下拉框,可以选择 App Heap、Image Heap 和 Zygote Heap,对应的就是上篇博客讲到的 Allocation Space,Image Space 和 Zygote Space,我们这里选择 Allocation Space,然后第二个选择 PackageTreeView 这一项,展开之后就能看见一个树形结构了,然后继续展开我们应用包名的对应对象,就可以很清晰的看到有多少个 Activity 对象了,上面那两栏展示的信息按照从左到右的顺序,定义如下所示:
Column | Description |
---|---|
Class Name | 占有这块内存的类名 |
Total Count | 未被处理的数量 |
Heap Count | 在上面选择的指定 heap 中的数量 |
Sizeof | 这个对象的大小,如果在变化中,就显示 0 |
Shallow Size | 在当前这个 heap 中的所有该对象的总数 |
Retained Size | 这个类的所有对象占有的总内存大小 |
Instance | 这个类的指定对象 |
Reference Tree | 指向这个选中对象的引用,还有指向这个引用的引用 |
Depth | 从 GC Root 到该对象的引用链路的最短步数 |
Shallow Size | 这个引用的大小 |
Dominating Size | 这个引用占有的内存大小 |
然后可以点击展开右侧的 Analyzer Tasks 项,勾选上需要检测的任务,然后系统就会给你分析出结果:
// 待补充图片
从分析的结果可以看到泄漏的 Activity 有两个,非常直观,然后点开其中一个,观察下面的 ReferenceTree 选项:
// 待补充图片
可以看到 Thread 对象持有了 SecondActivity 对象的引用,也就是 GC Root 持有了该 Activity 的引用,导致这个 Activity 无法回收,问题的根源我们就发现了,接下来去处理它就好了。
5.MAT
MAT(Memory Analyzer Tools)是一个 Eclipse 插件,它是一个快速、功能丰富的 Java heap 分析工具,它可以帮助我们查找内存泄漏和减少内存消耗,MAT 插件的下载地址:Eclipse Memory Analyzer Open Source Project,上面通过 Android studio 生成的 .hprof 文件因为格式稍有不同,所以需要经过一个简单的转换,然后就可以通过 MAT 去打开了: 通过 MAT 去打开转换之后的这个文件:
用的最多的就是 Histogram 功能,点击 Actions 下的 Histogram 项就可以得到 Histogram 结果:
我们可以在左上角写入一个正则表达式,然后就可以对所有的 Class Name 进行筛选了,很方便,顶栏展示的信息 “Objects” 代表该类名对象的数量,剩下的 “Shallow Heap” 和 “Retained Heap” 则和 Android Memory Monitor 类似。咱们接着点击 SecondActivity,然后右键:
在弹出来的菜单中选择 List objects->with incoming references 将该类的实例全部列出来:
通过这个列表我们可以看到 SecondActivity@0x12faa900 这个对象被一个 this$00x12c65140 的匿名内部类对象持有,然后展开这一项,发现这个对象是一个 handler 对象:
快速定位找到这个对象没有被释放的原因,可以右键 Path to GC Roots->exclude all phantom/weak/soft etc. references 来显示出这个对象到 GC Root 的引用链,因为强引用才会导致对象无法释放,所以这里我们要排除其他三种引用:
这么处理之后的结果就很明显了:
一个非常明显的强引用持有链,GC Root 我们前面的博客中说到包含了线程,所以这里的 Thread 对象 GC Root 持有了 SecondActivity 的引用,导致该 Activity 无法被释放。
MAT 还有一个功能就是能够对比两个 .hprof 文件,将两个文件都添加到 Compare Basket 里面: 添加进去之后点击右上角的 ! 按钮,然后就会生成两个文件的对比:
同样适用正则表达式将需要的类筛选出来:
结果也很明显,退出 Activity 之后该 Activity 对象未被回收,仍然在内存中,或者可以调整对比选项让对比结果更加明显:
也可以对比两个对象集合,方法与此类似,都是将两个 Dump 结果中的对象集合添加到 Compare Basket 中去对比,找出差异后用 Histogram 查询的方法找出 GC Root,定位到具体的某个对象上。
6.LeakCanary
LeakCanary可能是上面几个工具中最好用的了,用法如下:
在build.gradle文件中添加如下依赖:
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
}
然后在application中添加如下代码:
public class ExampleApplication extends Application {
@Override public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
// Normal app init code...
}
}
OK,配置完成!噼里啪啦多点开几个页面,看看LeakCanary是否已经全都暴露出来了。
最后
以上就是隐形煎饼为你收集整理的分析内存泄漏的工具与方法1.adb shell && Memory Usage的全部内容,希望文章能够帮你解决分析内存泄漏的工具与方法1.adb shell && Memory Usage所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复