Android内存泄露案例分析

来源:互联网 发布:淘宝卖切糕 编辑:程序博客网 时间:2024/04/29 03:40
一款优秀的Android应用,不仅要有完善的功能,也要有良好的体验,而性能是影响体验的一个重要因素。内存泄露是Android开发中常见的性能问题。这篇文章,通过我们曾经遇到的一个真实的案例,来讲述一个内存泄露问题,从发现到分析定位,再到最终解决的全过程。
这里把整个过程分为四个阶段:
第一阶段,现场勘查,分析Bug现象,找出有用线索;
第二阶段,初步推断,根据之前的线索,推断可能导致Bug的原因,并且进一步验证推断是否正确;
第三阶段,探究根源,找出导致Bug的真正原因;
第四阶段,解决方案,研究如何解决问题。
现场勘查
之前我们开发过一款应用,交给QA测试之后,发现有时候界面会卡顿,动画不流畅。经过反复测试找到了规律,当连续多次打开应用时,问题就会出现。我们根据这个方式重现Bug时,又发现Logcat中频繁输出GC日志(如图一所示)。 

图一
这里先简单介绍一下GC,也就是垃圾回收机制,Android通过提供垃圾回收机制来管理内存,当内存不足时会触发垃圾回收,回收没用的对象,释放内存。我们通过两张图(图二、三)来看一下垃圾回收的过程。

这里GC Roots表示垃圾回收器对象,每个节点表示内存中的对象,箭头表示对象之间的引用关系,能被GC Roots直接或者间接引用到的对象ABCD,表示正在使用的对象,不能被引用到的EFG是无用对象,垃圾回收时就会被回收掉。当系统触发一次垃圾回收时,对象EFG就会被回收。
以上就是垃圾回收的过程。在现场勘查这一阶段,我们找到两条非常有用的线索:
线索一:连续多次打开应用之后,界面卡顿,动画不流畅;
线索二:操作过程中,LogCat频繁输出GC日志。
初步推断
现在到第二阶段,根据前一阶段找到的线索,当连续多次打开应用,界面卡顿,同时Logcat不断输出GC日志,初步推测我们的应用中存在内存泄漏。首先我们先看一下什么是内存泄露呢?我们通过两张图来演示,如图四和五。
  
这两张图跟刚才演示GC过程的图很像,这时候再触发GC时,EG会被回收,F对于应用来说虽然无用了,却无法被回收,最后导致了内存泄漏。
因此,很可能每次打开应用时,都会产生像F这样的对象,导致内存占用越来越高,系统频繁触发GC。
验证推断
接下来就要去验证,这里我们利用DDMS工具。
DDMS是虚拟机调试监控服务,它能帮助我们测试设备截屏,设置虚拟地理坐标,针对特定的进程查看它的堆信息等等。
如何利用它来验证我们的推断呢?首先要Load出应用的内存快照,这里分为四步。第一步选中我们要查看的应用,第二步点击Update Heap按钮,这时候DDMS就会通知应用准备收集内存信息,第三步选择Heap标签,Heap标签页能够展示出内存的所有信息。第四步点击Cause GC,这时候就会把内存快照Load出来。具体操作如图六。 

图六
Load出内存信息之后,就来分析我们应用中是否存在内存泄漏,分析内存泄漏的关键数据之一,就是Total Size。
重复打开应用时,如果不存在内存泄漏的话,Total Size只会在一定范围内波动。如果我们的推断正确,连续打开应用,Total Size会持续增加。接着我们就来测试分析,连续打开应用,如图七。

图七
这里展示了第一次、第二次、第三次,以及第十次打开时Total size的截图,Total Size一直在增大,其中1-byte array增大最为明显,1-byte array表示的是byte[],或者boolean[]类型的数组。所以我们能够得出结论:打开应用时,确实存在内存泄漏。
探究根源
确认了问题,接下来就要探究问题的根源。每一个应用运行过程中,都会持有上万甚至百万个对象,我们就要分析这些对象在内存中的状态,看哪些对象对应用来说已经没用了,但是还在占用着内存。
0 0