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

相关文章

来自专栏java闲聊

WebMagic初探,了解爬虫

1423
来自专栏更流畅、简洁的软件开发方式

我写项目的步骤。抛砖引玉。

相信各位高手都会有自己习惯或者独特的写项目的方式,不知道是否可以拿出来和大家分享一下。 我先说一下我的,就当作是一块大砖头吧。 1、需求调研、分析 2...

2068
来自专栏gaoqin31

Linux 的账号与群组

理员的工作中,相当重要的一环就是『管理账号』啦!因为整个系统都是你在管理的, 并且所有一般用户的账号申请,都必须要透过你的协助才行!所以你就必须要了解一下如何管...

531
来自专栏大宽宽的碎碎念

聊聊BIO,NIO和AIO (1)到底什么是“IO Block”BIONIOIO多路复用用epoll实现的IO多路复用epoll的优势水平触发和边沿触发再来思考一下什么是“Block”总结

3028
来自专栏醒者呆

【精解】EOS TPS 多维实测

由于我们在研究eos阶段,大量使用到cleos,因此使用cleos来测试tps是我们第一个能想到的手段。这一节我们将加深理解tps的意义,tps的计算方法,讨论...

1634
来自专栏jojo的技术小屋

原 微信小程序踩坑录(canvas、pos

5837
来自专栏jojo的技术小屋

原 微信小程序踩坑录(canvas、pos

作者:汪娇娇 日期:2016.11.24 现在也不知道距离微信公测多少日子了,反正感觉我是埋在微信小程序这个坑里很久了,公司的项目终于快接近尾声,现在就腾点时间...

3175
来自专栏芋道源码1024

Dubbo源码解析 —— 服务引用原理

前言 经过上一篇dubbo源码解析-简单原理、与spring融合的铺垫,我们已经能简单的实现了dubbo的服务引用.其实上一篇中的代码,很多都是从dubbo源码...

2808
来自专栏Golang语言社区

nwui —— 又一个go语言图形界面解决方案

Github: https://github.com/go-nwui/nwui 最近开的一个大坑,具体实现就是自动生成htm+css+js然后调用nw.js来显...

2716
来自专栏木子昭的博客

Python3获取本机公网ip(爬虫法)

今天试用了google的python在线编程工具colab,确实很好用,当时好奇在线环境的主机ip是多少? 在网上查了半小时的方法后, 都不好用,后来灵机一动...

3765

扫码关注云+社区