工具篇-MAT(Memory Analyzer Tool)

时间:2022-05-24 01:27:52

---
layout: post
title: 工具篇-MAT(Memory Analyzer Tool)
description: 让内存泄漏无所遁形 2015-10-08
category: blog
---

## 让内存泄漏无所遁形##

----------

### 一、android 垃圾回收机制的理解 ###
**1、堆和栈** - 至少我们需要知道,我们写的一句代码,它的所需要的内存是放到哪里的。
至于如何释放堆和栈这些细节,此次略过一万字,请大家自行google.
    
堆heap:内存数据区;简单来说,保存了对象的实例:对象的属性值;对象从堆申请完内存之后会生成一个地址存在stack里边。heap的释放是有jvm回收器来负责的。

栈stack:内存指令区;简单来说,保存了对象的实例里边的属性类型,方法地址,基本类型,如int,float,常量,静态变量都是存在栈里边的。栈里边内存的释放是FILO(first in ,last out)会自动释放。

简单来说,new 创建的一个对象,大部分内存都是放在heap里的。

native 内存 和 ashmem 是不属于jvm gc范围的;

**2、拉贾回收器**

垃圾收集算法的核心思想是:对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配。

触发条件:

- 手动调用System.gc(),只是建议触发GC,并不保证每次都能GC
 - app空闲时,gc的线程优先级是极低的,此时可能会调用GC
 - heap内存申请不足时,会强制触发gc,若GC一次还不够,则会进行第二次GC,若内存还不够,则会引发OOM;
**GC引发的直观问题,频繁的GC会导致app的用户体验:UI卡顿,耗电;**

由GC想起的一些编码细节:

(1)能用int就不要用Integer
        (2)大对象不用时最好设置为null
        (3)尽量避免产出大量的临时对象
        (4)使用StringBuffer代替String拼接字符串,String拼接会产生多个垃圾对象
        (5)尽量少使用静态变量,静态变量属于全局变量,不会被GC回收,它们会一直占用内存。
        (6)activity切记不要被全局单例类引用。

----------

###二、MAT-Memory Analyzer tool ###
此工具可以帮我们分析出当前使用的app存留在内存里对象有哪些,并能跟踪到具体的引用对象,从而找出内存泄漏的原因。

1、MAT下载地址:[点击进入](http://www.eclipse.org/mat/downloads.php)

2、了解几个参数的含义:

DDMS参数:
        a、Heap Size 系统为此进程所分配堆的大小,当资源增加,
          当前堆的空余空间不够时,系统会增加堆的大小,
          若超过上限 (例如64M,视平台和具体机型而定)则会被杀掉
        b、Allocated 堆中已分配的大小,这是应用程序实际占用的内存大小,资源回收后,此项数据会变小

MAT工具参数:
        Histogram:列出当前存留对象和数量
        Dominator Tree:由大到小列举出对象以及他们含有什么。
        Path to GC Root:显示GC路径的path
        
![此处输入图片的描述](http://7xnby9.com1.z0.glb.clouddn.com/actions.jpg)

###二、MAT-Memory Analyzer tool 使用步骤 ###

1、dump内存文件:

使用DDMS的dump工具dump出你所选中进程的文件;

步骤:
    1、选择进程,点击update heap;
    2、再手机上打开你想要检测的页面
    3、狂戳GC直到Allocated的值不再下降为止;
    4、再点击一下update heap带有红色向下箭头的按钮,稍等片刻,会自动弹出文件保存框;
    5、保存dump文件到本地;
![此处输入图片的描述](http://7xnby9.com1.z0.glb.clouddn.com/dump.jpg)
![此处输入图片的描述](http://7xnby9.com1.z0.glb.clouddn.com/heap.jpg)

注意:
    1、eclipse自带的mat插件可自动转换dump文件格式,可直接用mat工具打开;
    2、IDEA或者android studio dump的出的hprof文件是需要转换的
      转换命令是:hprof-conv src.hprof dest.hprof,完了之后再用mat工具打开;
打开dump文件后如下图:
![此处输入图片的描述](http://7xnby9.com1.z0.glb.clouddn.com/over_view.jpg)

2、Dominator Tree如下图:
![此处输入图片的描述](http://7xnby9.com1.z0.glb.clouddn.com/dominator_tree.jpg)
点击之后如图,此时右键图选中行-》List Objects->with outgoing refrences找到外部应用的对象,如下图:
![此处输入图片的描述](http://7xnby9.com1.z0.glb.clouddn.com/outgoing_two.jpg)
此处找到了Bitmap的引用对象,然后右键-》GC ROOT PATHS-》exclude weak/soft....排除软弱引用找到强引的对象,如下图:
![此处输入图片的描述](http://7xnby9.com1.z0.glb.clouddn.com/topic_mengban.jpg)
到这里就能知道是哪里的业务代码导致了内存泄漏,从图中可看出
mBitmap来源于ImageView
ImageView来源-ll_mengban的child
ll_mengban来源-TopicDetailActivity的控件
TopicDetailActivity context被 mmsdk引用了;
从而导致mBitmap无法释放;
然后我们来看看TopicDetailActivity是如何被强引用的:
在TopicDetailActivity的onCreate里执行了
        
        shareController = new ShareController(this);
然而ShareController执行了这个:
        
        public ShareController(Activity activity) {
            mActivity = activity;
            ShareLoginController.getInstance(mActivity).addLoginListener(this);
        }
然而ShareLoginController执行了这个:

public ShareController(Activity activity) {
            mActivity = activity;
            ShareLoginController.getInstance(mActivity).addLoginListener(this);
        }

然而ShareLoginController构造方法执行了这个:

public ShareLoginController(Activity activity) {
            if (mController == null) {
                try {
                    mController =(UMSocialService) BeanManager.getSocialService(activity);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if(loginListeners==null){
                loginListeners = new ArrayList<IShareLoginListener>();
            }
        }

然而UMSocialService里把context传递给了QQsdk,导致了一直被强引用无法GC的原因;

找到了问题,解决办法就是去除这个强引用即可;

-----------------------
博客:http://iceAnson.github.io

http://iceanson.github.io/tool-mat/