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 条评论
登录 后参与评论

相关文章

来自专栏信安之路

代码审计之 zzzphp

想想很久都没有发布代码审计的文章了,最近忙于开发任务加上最近状态不太好,哎研发dog。

723
来自专栏WeTest质量开放平台团队的专栏

低于0.01%的极致Crash率是怎么做到的?

2554
来自专栏双十二技术哥

一个Android路由框架的诞生之路

在这个过程中一直强调了组件化的一个基础设施:路由!没有它组件化可以说是寸步难行,本篇文章我们就来谈谈一个组件化路由框架诞生过程中的那些思考。

902
来自专栏北京马哥教育

Python Unicode编码混乱 :来自大洋彼岸的怨念

Unicode已经解决了很多问题。知晓ISO-8859-*和CP437带来的混乱(当然对于非西方语言来说更糟糕)的人都可以证明这一点。当然,这些天他们正在做一项...

1084
来自专栏FreeBuf

Frida在爆破Windows程序中的应用

谈到爆破,相信大部分网络安全从业者都并不陌生,爆破爆破,就是暴力破解嘛。通过枚举尝试尽可能多的可能解,再进行验证判断是否正确。在进行web的爆破时,我们通常会使...

802
来自专栏数据和云

Oracle写错误与文件离线

当Oracle写数据文件遇到错误时,该如何应对呢?是离线文件还是崩溃实例?这个简单问题的技术变化跨度超过了20年。 自Oracle 11.2.0.2版本开始...

2523
来自专栏Ryan Miao

做项目中没经验遇到的各种问题

1.java基础不牢,关于对象和类的生命周期等不明白 在做app登陆token验证的时候。我设计一个tokenutil中添加一个成员变量map,每次登陆成功,将...

2898
来自专栏LeoXu的博客

[翻译]使用 Velocity 构建一个稳定安全的Web应用

<p> draft document -- 2003年6月11日 </p> <p> 作为一名web开发者,任何时候当你构建一个Web应用时,有责任确保你的应用程...

862
来自专栏安恒网络空间安全讲武堂

2018红帽杯线下攻防赛Web总结

2143
来自专栏Android-JessYan

一行代码实现Okhttp,Retrofit,Glide下载上传进度监听

原文地址: http://www.jianshu.com/p/5832c776621f qq群:301733278

902

扫码关注云+社区