### 15.2 内存泄露分析之MAT工具
MAT的全称是Eclipse Memory Analyzer,它是一款强大的内存泄露分析工具,MAT不需要安装,下载后解压即可使用,下载地址为http://www.eclipse.org/mat/downloads.php。对于Eclipse来说,MAT也有插件版,但是不建议使用插件版,因为独立版使用起来更加方便,即使不安装Eclipse也可以正常使用,当然前提是有内存分析后的hprof文件。
为了采用MAT来分析内存泄露,下面模拟一种简单的内存泄露情况,下面的代码肯定会造成内存泄露:
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private static Context sContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sContext = this;
}
}
编译安装,然后打开DDMS界面,其中AndroidStudio的DDMS位于Monitor中。接着用鼠标选中要分析的进程,然后使用待分析应用的一些功能,这样做是为了将尽量多的内存泄露暴露出来,然后单击Dump HPROF file这个按钮(对应图15-1中底部有黑线的按钮),等待一小段时间即可导出一个hprof后缀的文件,如图15-1所示。
:-: ![](https://img.kancloud.cn/2e/d5/2ed533218cae855b910a07bcbe3a736b_1361x528.png)
图15-1 DDMS视图
导出hprof文件后并不能使用它来进行分析,因为它不能被MAT直接识别,需要通过hprof-conv命令转换一下。hprof-conv命令是Android SDK提供的工具,它位于Android SDK的platform-tools目录下:
hprof-conv com.ryg.chapter_15.hprof com.ryg.chapter_15-conv.hprof
当然如果使用的是Eclipse插件版的MAT的话,就可以不进行格式转换了,可以直接用MAT插件打开。
经过了上面的步骤,接下来就可以直接通过MAT来进行内存分析了。打开MAT,通过菜单打开刚才转换后的com.ryg.chapter_15-conv.hprof这个文件,打开后的界面如图15-2所示。
:-: ![](https://img.kancloud.cn/3d/a3/3da31abd2c291b059203c376790abdcb_967x611.png)
图15-2 MAT的内存分析主界面
如图15-2所示,MAT提供了很多功能,但是最常用的只有Histogram和Dominator Tree,通过Histogram可以直观地看出内存中不同类型的buffer的数量和占用的内存大小,而Dominator Tree则把内存中的对象按照从大到小的顺序进行排序,并且可以分析对象之间的引用关系,内存泄露分析就是通过Dominator Tree来完成的。图15-3和图15-4分别是MAT中Histogram和Dominator Tree的界面。
:-: ![](https://img.kancloud.cn/ed/24/ed24b6b23e0f5ced8ce4cf9daa77568e_970x607.png)
图15-3 MAT中Histogram的界面
:-: ![](https://img.kancloud.cn/e5/b9/e5b964fc23ba0fff0be423d6502951eb_965x606.png)
图15-4 MAT中Ddominator Tree的界面
为了分析内存泄露,我们需要分析Dominator Tree里面的内存信息,在Dominator Tree中内存泄露的原因一般不会直接显示出来,这个时候需要按照从大到小的顺序去排查一遍。一般来说Bitmap泄露往往都是由于程序的某个地方发生了内存泄露都引起的,在图15-4中的第2个结果就是一个Bitmap泄露,选中它然后单击鼠标右键->Path To GC Roots->exclude wake/soft references,如图15-5所示。可以看到sContext引用了Bitmap最终导致了Bitmap无法释放,但其实根本原因是sContext无法释放所导致的,这样我们就找出了内存泄露的地方。Path To GC Roots过程中之所以选择排除弱引用和软引用,是因为二者都有较大几率被gc回收掉,它们并不能造成内存泄露。
:-: ![](https://img.kancloud.cn/e0/07/e007163175263f90b66318f210ccaf2f_1338x600.png)
图15-5 Path To GC Roots后的结果
在Dominator Tree界面中是可以使用搜索功能的,比如我们尝试搜索MainActivity,因为这里我们已经知道MainActivity存在内存泄露了,搜索后的结果如图15-6所示。我们发现里面有6个MainActivity的对象,这是因为每次按back键退出再重新进入MainActivity,系统都会重新创建一个新的MainActivity,但是由于老的MainActivity无法被回收,所以就出现了多个MainActivity对象的情形。另外MAT还有很多其他功能,这里就不再一一介绍了,请读者自己体验吧。
:-: ![](https://img.kancloud.cn/a9/64/a964f9c1bcb4026f174f7a23afde7071_1110x610.png)
图15-6 Dominator Tree的搜索功能
- 前言
- 第1章 Activity的生命周期和启动模式
- 1.1 Activity的生命周期全面分析
- 1.1.1 典型情况下的生命周期分析
- 1.1.2 异常情况下的生命周期分析
- 1.2 Activity的启动模式
- 1.2.1 Activity的LaunchMode
- 1.2.2 Activity的Flags
- 1.3 IntentFilter的匹配规则
- 第2章 IPC机制
- 2.1 Android IPC简介
- 2.2 Android中的多进程模式
- 2.2.1 开启多进程模式
- 2.2.2 多进程模式的运行机制
- 2.3 IPC基础概念介绍
- 2.3.1 Serializable接口
- 2.3.2 Parcelable接口
- 2.3.3 Binder
- 2.4 Android中的IPC方式
- 2.4.1 使用Bundle
- 2.4.2 使用文件共享
- 2.4.3 使用Messenger
- 2.4.4 使用AIDL
- 2.4.5 使用ContentProvider
- 2.4.6 使用Socket
- 2.5 Binder连接池
- 2.6 选用合适的IPC方式
- 第3章 View的事件体系
- 3.1 View基础知识
- 3.1.1 什么是View
- 3.1.2 View的位置参数
- 3.1.3 MotionEvent和TouchSlop
- 3.1.4 VelocityTracker、GestureDetector和Scroller
- 3.2 View的滑动
- 3.2.1 使用scrollTo/scrollBy
- 3.2.2 使用动画
- 3.2.3 改变布局参数
- 3.2.4 各种滑动方式的对比
- 3.3 弹性滑动
- 3.3.1 使用Scroller7
- 3.3.2 通过动画
- 3.3.3 使用延时策略
- 3.4 View的事件分发机制
- 3.4.1 点击事件的传递规则
- 3.4.2 事件分发的源码解析
- 3.5 View的滑动冲突
- 3.5.1 常见的滑动冲突场景
- 3.5.2 滑动冲突的处理规则
- 3.5.3 滑动冲突的解决方式
- 第4章 View的工作原理
- 4.1 初识ViewRoot和DecorView
- 4.2 理解MeasureSpec
- 4.2.1 MeasureSpec
- 4.2.2 MeasureSpec和LayoutParams的对应关系
- 4.3 View的工作流程
- 4.3.1 measure过程
- 4.3.2 layout过程
- 4.3.3 draw过程
- 4.4 自定义View
- 4.4.1 自定义View的分类
- 4.4.2 自定义View须知
- 4.4.3 自定义View示例
- 4.4.4 自定义View的思想
- 第5章 理解RemoteViews
- 5.1 RemoteViews的应用
- 5.1.1 RemoteViews在通知栏上的应用
- 5.1.2 RemoteViews在桌面小部件上的应用
- 5.1.3 PendingIntent概述
- 5.2 RemoteViews的内部机制
- 5.3 RemoteViews的意义
- 第6章 Android的Drawable
- 6.1 Drawable简介
- 6.2 Drawable的分类
- 6.2.1 BitmapDrawable2
- 6.2.2 ShapeDrawable
- 6.2.3 LayerDrawable
- 6.2.4 StateListDrawable
- 6.2.5 LevelListDrawable
- 6.2.6 TransitionDrawable
- 6.2.7 InsetDrawable
- 6.2.8 ScaleDrawable
- 6.2.9 ClipDrawable
- 6.3 自定义Drawable
- 第7章 Android动画深入分析
- 7.1 View动画
- 7.1.1 View动画的种类
- 7.1.2 自定义View动画
- 7.1.3 帧动画
- 7.2 View动画的特殊使用场景
- 7.2.1 LayoutAnimation
- 7.2.2 Activity的切换效果
- 7.3 属性动画
- 7.3.1 使用属性动画
- 7.3.2 理解插值器和估值器 /
- 7.3.3 属性动画的监听器
- 7.3.4 对任意属性做动画
- 7.3.5 属性动画的工作原理
- 7.4 使用动画的注意事项
- 第8章 理解Window和WindowManager
- 8.1 Window和WindowManager
- 8.2 Window的内部机制
- 8.2.1 Window的添加过程
- 8.2.2 Window的删除过程
- 8.2.3 Window的更新过程
- 8.3 Window的创建过程
- 8.3.1 Activity的Window创建过程
- 8.3.2 Dialog的Window创建过程
- 8.3.3 Toast的Window创建过程
- 第9章 四大组件的工作过程
- 9.1 四大组件的运行状态
- 9.2 Activity的工作过程
- 9.3 Service的工作过程
- 9.3.1 Service的启动过程
- 9.3.2 Service的绑定过程
- 9.4 BroadcastReceiver的工作过程
- 9.4.1 广播的注册过程
- 9.4.2 广播的发送和接收过程
- 9.5 ContentProvider的工作过程
- 第10章 Android的消息机制
- 10.1 Android的消息机制概述
- 10.2 Android的消息机制分析
- 10.2.1 ThreadLocal的工作原理
- 10.2.2 消息队列的工作原理
- 10.2.3 Looper的工作原理
- 10.2.4 Handler的工作原理
- 10.3 主线程的消息循环
- 第11章 Android的线程和线程池
- 11.1 主线程和子线程
- 11.2 Android中的线程形态
- 11.2.1 AsyncTask
- 11.2.2 AsyncTask的工作原理
- 11.2.3 HandlerThread
- 11.2.4 IntentService
- 11.3 Android中的线程池
- 11.3.1 ThreadPoolExecutor
- 11.3.2 线程池的分类
- 第12章 Bitmap的加载和Cache
- 12.1 Bitmap的高效加载
- 12.2 Android中的缓存策略
- 12.2.1 LruCache
- 12.2.2 DiskLruCache
- 12.2.3 ImageLoader的实现446
- 12.3 ImageLoader的使用
- 12.3.1 照片墙效果
- 12.3.2 优化列表的卡顿现象
- 第13章 综合技术
- 13.1 使用CrashHandler来获取应用的crash信息
- 13.2 使用multidex来解决方法数越界
- 13.3 Android的动态加载技术
- 13.4 反编译初步
- 13.4.1 使用dex2jar和jd-gui反编译apk
- 13.4.2 使用apktool对apk进行二次打包
- 第14章 JNI和NDK编程
- 14.1 JNI的开发流程
- 14.2 NDK的开发流程
- 14.3 JNI的数据类型和类型签名
- 14.4 JNI调用Java方法的流程
- 第15章 Android性能优化
- 15.1 Android的性能优化方法
- 15.1.1 布局优化
- 15.1.2 绘制优化
- 15.1.3 内存泄露优化
- 15.1.4 响应速度优化和ANR日志分析
- 15.1.5 ListView和Bitmap优化
- 15.1.6 线程优化
- 15.1.7 一些性能优化建议
- 15.2 内存泄露分析之MAT工具
- 15.3 提高程序的可维护性