Android内存泄露分析(MemoryAnalyzer工具)

前提条件:


1,电脑安装了Java 运行环境  


2,手机端开启了 USB 调试开关 


3,获取 root 权限


基本步骤:


1,使用eclipse 自带的 DDMS 工具分析各线程的内存使用情况,如下图所示






Heap视图界面会定时刷新,在对应用的不断的操作过程中就可以看到内存使用的变化。
怎样判断当前进程是否有内存泄漏呢?


这里需要注意一个值:VM Heap页面中部有一个data object选项,即数据对象,也就是我们的程序中大量存在的类类型的对象。


在data object一行中有一列是“Total Size”,其值就是当前进程中所有Java数据对象的内存总量,一般情况下,这个值的大小决定了是否会有内存泄漏。如上图中选中行所示。


可以据此判断内存有泄漏:
1) 不断的操作当前应用,或者重复某一动作,注意观察data object的Total Size值。




2) 正常情况下Total Size值都会稳定在一个有限的范围内,也就是说如果程序中的的代码逻辑良好,


没有创建的对象不被GC机制正常回收的情况,即便 我们不断的操作生成很多对象,而在虚拟机不断的进行垃圾回收的过程中,这些对象都被正常回收了,内存使用量会保持在一个比较稳定的水平。




3) 如果代码中存在对象引用没有释放的情况,则data object的Total Size值在每次GC后不会有明显的回落,随着操作次数的增多Total Size的值会越来越大。
正常情况下,一个虚拟机的进程的内存在64M, 如果内存泄漏会发现 Heap Size 在不断的逼近 64M, 一旦达到这个值时,就会出现退出应用等情况。




发生内存泄露,Total Size的值越来越大时,按下“Dump HPROF file”按钮,这个时候会提示设置hprof文件的保存路径。保存后,可以对比log来分析是哪些操作造成了内存泄漏。


2,点击 按钮,导出 hprof 文件,使用MAT 工具进行分析。具体分析步骤和过程详见下面链接


http://www.ibm.com/developerworks/cn/opensource/os-cn-ecl-ma/index.html
3,打开 MAT 工具,File-->Open Heap Dump... 选择你刚刚保存的 hprof 文件打开


此时,会弹出一个错误,如下图所示:




提示:  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 分析内存使用的主界面:




点击上图中的 Reports -->Leak Suspects 则可以进一步看到更详细的内存泄漏疑点。




在其中怀疑的地方,点击 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等其他有自己声明周期的对象来说,直接引用都需要谨慎考虑是否会存在内存泄露的可能。()
[java] view plaincopy
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;  
            }  
        }  
}  
[java] view plain copy
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 保持了一个长生命周期的引用。


[java] view plaincopy
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);  
}  
[java] view plain copy
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);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android内存分析是指通过分析应用程序在运行过程中的内存使用情况,找出内存泄漏、内存溢出等问题,以优化应用程序的性能和稳定性。常用的工具Android Studio提供的内存分析器和MAT(Memory Analyzer Tool)等。 在进行Android内存分析时,可以采取以下步骤: 1. 监测内存使用:使用Android Studio提供的内存监视工具,观察应用程序在运行过程中的内存使用情况,包括堆内存和非堆内存的使用情况。 2. 寻找内存泄漏:通过观察内存使用情况,查找是否有对象没有被正确释放,从而导致内存泄漏。可以使用内存分析器来分析堆快照,查找对象引用关系,找出不再需要的对象。 3. 优化内存占用:观察哪些对象占用了大量内存,并尝试优化其内存占用。例如,可以考虑使用弱引用或软引用来管理对象,减少不必要的缓存等。 4. 避免内存溢出:注意合理管理大数据集合、避免频繁创建大对象、及时释放不需要的资源等,以避免应用程序因为内存溢出而崩溃。 5. 使用内存分析工具Android Studio提供了内存分析器,可以帮助开发者分析内存使用情况,找出内存泄漏和优化内存占用。MAT是一款Java堆内存分析工具,也可用于Android内存分析。 通过进行Android内存分析,开发者可以及时发现和解决应用程序的内存问题,提升应用程序的性能和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值