项目过程中,有时候OOM让你猝不及防,不过出现了错误,就需要查找原因,所以就用到这个工具,安装比较麻烦,贴出来和大家共享一下:
第一步:下载Eclipse MAT
下载地址:http://www.eclipse.org/mat/downloads.php
![MAT Memory Analyzer Tool 插件装配(图解) - 小蟹米 - 小蟹米](http://img2.ph.126.net/llC-W5bWidYwTZu7ljDIaw==/6597714080472030542.jpg)
第二步:下载之后将压缩包解压,放置到Myeclipse 的\MyEclipse\dropins目录下
![MAT Memory Analyzer Tool 插件装配(图解) - 小蟹米 - 小蟹米](http://img2.ph.126.net/vBEL-TyRl9KWUWR9Cs0t0A==/1916844591499627013.jpg)
第三步:在dropins文件夹下面新建一个mat.link的文件。
![MAT Memory Analyzer Tool 插件装配(图解) - 小蟹米 - 小蟹米](http://img0.ph.126.net/cCFm2TJmeZ6U_YgnoHeS9A==/1381760660772662979.jpg)
mat.link内容(指向刚才解压后的文件夹名称):
path=D:\\MyEclipse\\MyEclipse\\dropins\\MemoryAnalyzer-1.3.0.201305170842
第四步:重启Myeclipse.打开DDMS后台,再Devices选择你的项目,点击"dump HPROF file"按钮导出 hprof 文件,然后使用MAT分析下载下来的文件。
第五步:打开 MAT 工具,File-->Open Heap Dump... 选择你刚刚保存的 hprof 文件打开
此时,会弹出一个错误,如下图所示:
![MAT Memory Analyzer Tool 插件装配(图解) - 小蟹米 - 小蟹米](http://img1.ph.126.net/EEcvMD9zVNW7uKnqIdZ2JQ==/6598173676332238381.png)
提示: Unknown HPROF Version (JAVA PROFILE 1.0.3) (java.io.IOException)
哦,不要以为是 MAT 工具版本不对,其实是 android 的 hprof 文件在这里需要进行转换一下格式才可以使用 MAT 打开,不知道 谷歌在这里
捣了什么鬼,难道是优化?
使用 android sdk 目录下的 tools 中一个工具进行转化一下
4,使用AndrodiSDK/tools/hprof-conv转化hprof文件,
首先,要通过控制台进入到你的 android sdk tools 目录下
例如 hprof-conv input.hprof out.hprof
再使用MAT工具打开转换后的 hprof 文件,就能看到完整的内存使用分析报告了。
如下所示是 MAT 分析内存使用的主界面:
在其中怀疑的地方,点击 Details 就可以看到具体的内存使用情况了。
tip1:
有一种比较好的方法是,在内存泄漏开始时抓取一个 hprof 文件,在内存泄漏很厉害时,app 濒临崩溃时再抓取一个hprof 文件。
对比看这两个图,就很容易看出来上面的饼图中哪一块存在内存泄漏。
有的时候能直接看出来多了一块。那么我们就从那一块入手进行分析。比较快能得到结果。
tip2:
看 dominator_tree,可以从列表中 data_object 最多的几项数据入手分析,如下文件所示(136,80对应的两项)
我这边曾经就因为在 onStart 中添加了一个 PhoneStateListener 的监听,而在 onStop 中未设置为空,导致内存泄漏。
这里引用一点别人总结的实例:
原因1:
BraodcastReceiver,ContentObserver,FileObserver,Cursor在Activity onDeatory或者某类声明周期结束之后一定要unregister或者close掉,否则这个Activity类会被system强引用,不会被内存回收。
原因2:
不要直接对Activity进行直接引用作为成员变量,如果不得不这么做,请用private WeakReference mActivity来做,相同的,对于Service等其他有自己声明周期的对象来说,直接引用都需要谨慎考虑是否会存在内存泄露的可能。()
- private static class MyHandler extends Handler {
- private WeakReference<GeneralSettings> mStatus;
- public MyHandler(GeneralSettings activity) {
- mStatus = new WeakReference<GeneralSettings>(activity);
- }
- @Override
- public void handleMessage(Message msg) {
- GeneralSettings status = mStatus.get();
- if (status == null) {
- return;
- }
- switch (msg.what) {
- case EVENT_UPDATE_STATS:
- status.updateTimes();
- sendEmptyMessageDelayed(EVENT_UPDATE_STATS, 1000);
- break;
- }
- }
- }
原因3:
对 Context 保持了一个长生命周期的引用。
- private static Drawable sBackground;
- @Override
- protected void onCreate(Bundle state) {
- super.onCreate(state);
- TextView label = new TextView(this);
- label.setText("Leaks are bad");
- if (sBackground == null) {
- sBackground = getDrawable(R.drawable.large_bitmap);
- }
- label.setBackgroundDrawable(sBackground);
- setContentView(label);
- }
sBackground的生命周期比Activity要长,label引用到context,sBackground又把label设为内部成员变量,所以sBackground引用到了context,导致activity结束的时候context还是不能释放,从而引发内存泄露。(不甚理解,还要仔细研究一下)
最后,作者给了一点总结(有些地方还是不太懂……)
总结:
1. 对activity的引用应该控制在activity的生命周期之内;
2. 如果不能就考虑使用getApplicationContext或者getApplication;
3. 尽量不要在静态变量或者静态内部类中使用非静态外部成员变量(包括context),即使要使用,也要考虑适时把外部成员变量置空(如上例可以通过把sBackground的callback置空来解决内存泄露的问题);也可以在内部类中使用弱引用来引用外部类的变量;
4. 做到在onDestroy中释放资源,如清空对图片等资源有直接引用或者间接引用的数组(使用array.clear();array = null).
参考:http://www.blogjava.net/rosen/archive/2010/06/13/323522.html
http://www.eoeandroid.com/forum.php?mod=viewthread&tid=96136