Android - 通过真实案例学习解内存泄漏问题,最终发现Android原生Bug

  作为一个Android新手小白,刚到新公司,最近的工作就是在学习解各类Bug。转型之初,面临各种新知识,会有压力,但是学习的过程是快乐的。

  上周刚遇上一类bug,就是应用的内存泄漏问题。最终通过前辈的指点,用了两天的时间(包括今天),来解决了这个问题,并最终发现了Android原生代码的bug(值得开心......)。因此将学习的过程总结出来,可以供像我一样的新人参考学习。

一. 问题发现的背景

   QA测试发现,多次打开Android系统中设置功能里的某个Activity时,其占用的资源未能释放,并且在两三百次的重复操作后,设置应用发生了Crash的现象。

   崩溃的原因是OOM问题,即占用的资源因未能被GC回收,导致内存不足,抛出了OOM(Out of Memory)的异常,应用发生Crash。

   因此下一步就是RD来解决问题啦!

二. 需要准备/掌握的工具

   没有工具的配合,你很难轻松的应对和解决问题。经过前辈的指导后,开始入手学习使用解此类问题的一系列工具。

1. Adb Shell 命令

   Android新手入门一定先从Adb开始,Adb全称是Android debug bridge,提供很多操作手机的命令,有了它,可以方便的debug问题。这里我们使用的命令如下,

A. adb shell

        进入adb shell,执行以下命令

B. am start -a "xxx" -d "xxx"

        通过activity manager打开activity,方便多次测试,调查进程内存占用情况

C. dumpsys meminfo xxx

        看进程的内存占用情况,xxx为包名

2. DDMS + MAT工具

       DDMS全称是Dalvik Debug Monitor Service,一般我用它来查看即时log,这里的作用是使用DDMS来生成hprof文件,hprof是Android进程的heap快照,有了它,可以来研究heap中存在哪些object,以及object的引用,研究为何GC没有回收对象的原因。

       而MAT工具,正是由Eclipse提供的,能方便分析hprof文件的工具。MAT全称是Memory Analyzer Tool,内存分析工具,安装方式是在Eclipse中,选择install new software,然后提供插件的网址,选择安装即可。

因此这里我们的思路是,通过Adb shell命令来测试并重现问题,然后用DDMS来抓取heap快照,使用MAT来分析heap快照,从来对照代码解决问题。

三. 解决此内存泄漏问题的过程

   1. 重现问题,通过am start命令直接打开此Activity,然后按手机的返回键,多次重复此过程

   2. 在步骤一的过程中,每次都使用dumpsys meminfo com.android.settings命令,来观察heap中Activity的数量。

  正常的情况下,Activity的值应该为0或1,不应该持续增长,因为按返回后,如果不存在内存泄漏,无用的Activity对象会被GC(垃圾回收)给回收掉。

  但这里,因为有问题存在,我观察到的现象是,Activity的数量一直在增长。如下图所示,heap中Activity的数量变化:

     步骤一操作1次,

         操作2次,

         操作5次,

       可以明显的看到问题的发生,即在我们每次操作过程中,Activity虽然已经通过返回键,不予显示,但是占用的资源未能被GC回收,每次操作都会生成一个新的不会且不会被释放的Activity对象,发生了内存泄漏!

       因此下一步要来解决问题。

3. 使用DDMS+MAT发现线索,解决问题

       既然现场已经重现,此时我们需要用DDMS来生成hprof文件,这里提到一点,如果你使用的都是Eclipse里安装的DDMS与MAT工具,在DDMS中点击生成hprof文件,会自动关联MAT,使用MAT打开此文件。

       DDMS生成hprof文件,点击下图中的2个绿色按钮,如下,

        MAT打开hprof文件,打开时建议选择第一项,如下,

        之后打开后,就能分析heap文件啦。这里我们选择,点击Dominator Tree,它能列出heap中最大的对象们,

       然后在打开的页面中,选择你测试时发现问题的Activity(可以使用关键词来过滤结果),这里出问题的Activity是,AppDrawOverlaySettingsActivity(Android原生代码),其对应的Fragment是DrawOverlayDetails。由于我们操作了5次,可以发现heap中的5个对象存在,都没有被释放。

       这时要分析其未被释放的原因,要使用到MAT的功能来分析对象的引用,因为强引用的对象不会被GC回收。既然这个Activity对象一直存在,就说明一定是有引用存在,导致其未被GC回收。

  我的做法是,右击object,点击Merge Shortest Paths to GC Roots -> exclude all phantom/week/soft etc. preferences(因为要排除弱引用,以及软引用,这些引用包含的对象都会被GC回收,对我们没有参考价值)。

  之后便可以发现原因了,

  通过查看其引用,发现存在一个可疑的mSession变量,它属于Activity的父类,在类中使用了当前的对象,但是一直未能释放,因此这就是问题的原因,导致GC未能回收资源。

  知道原因后,解决的方法便很简单,就是在按返回键,触发的onStory或onDetach方法中,释放此mSession对象。最终便能解决问题。

1    @Override
2     public void onDestroy() {
3         super.onDestroy();
4         mSession.release();
5     }

最后提交修改,在新的apk测试中,通过Adb shell命令测试发现,Activity数量已维持正常,内存泄漏的问题便也已解决。

最后总结,解决内存泄漏的问题,熟练使用命令和工具很重要。有了它们的帮助,能快速的找到线索,再到代码中去发现问题。当然复杂的问题,远没有本文中解决的过程简单,但是对于新手来说,学习此方法步骤会有很大帮助!

                                    - Kevin Song

                                    2016年5月9日

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Golang语言社区

【Golang语言社区】GO1.9 map并发安全测试

var m sync.Map //全局 func maintest() { // 第一个 YongHuomap := make(map[st...

5478
来自专栏杨龙飞前端

scrollto 到指定位置

2964
来自专栏张善友的专栏

LINQ via C# 系列文章

LINQ via C# Recently I am giving a series of talk on LINQ. the name “LINQ via C...

3015
来自专栏闻道于事

js登录滑动验证,不滑动无法登陆

js的判断这里是根据滑块的位置进行判断,应该是用一个flag判断 <%@ page language="java" contentType="text/html...

8718
来自专栏跟着阿笨一起玩NET

c#实现打印功能

3742
来自专栏大内老A

The .NET of Tomorrow

Ed Charbeneau(http://developer.telerik.com/featured/the-net-of-tomorrow/) Exciti...

39110
来自专栏一个会写诗的程序员的博客

Spring Reactor 项目核心库Reactor Core

Non-Blocking Reactive Streams Foundation for the JVM both implementing a Reactiv...

2822
来自专栏张善友的专栏

Silverlight + Model-View-ViewModel (MVVM)

     早在2005年,John Gossman写了一篇关于Model-View-ViewModel模式的博文,这种模式被他所在的微软的项目组用来创建Expr...

3308
来自专栏C#

DotNet加密方式解析--非对称加密

    新年新气象,也希望新年可以挣大钱。不管今年年底会不会跟去年一样,满怀抱负却又壮志未酬。(不过没事,我已为各位卜上一卦,卦象显示各位都能挣钱...)...

5988
来自专栏Ceph对象存储方案

Luminous版本PG 分布调优

Luminous版本开始新增的balancer模块在PG分布优化方面效果非常明显,操作也非常简便,强烈推荐各位在集群上线之前进行这一操作,能够极大的提升整个集群...

3675

扫码关注云+社区