Android程序的内存泄漏与规避方法
引言
本文引用地址:http://www.amcfsurvey.com/article/148153.htmAndroid应用程序中内存使用的问题经常容易被忽视,在传统的编程语言中(例如C语言),回收内存的任务是由程序本身来完成的,程序可以显式分配和释放变量所占用的内存。Android[1]应用程序采用Java编程语言编写,而Java区别于其他语言的一个重要优点就是它通过垃圾收集器(Garbage Collection,GC) 自动管理内存的回收,Java程序员只需通过内存分配操作创建对象,而无须关心对象占用的空间是如何被收回的。因此很多程序员认为在Java中不必担心内存泄漏的问题,然而实际并非如此,Java中仍然存在着内存泄漏。Android应用程序运行在嵌入式系统中,而嵌入式系统中内存的总量非常有限,因此如何合理地规避“内存泄露”问题也就显得十分关键。
1 造成Android应用程序内存泄漏的原因
1.1 引用没释放造成的内存泄露
(1) 注册没有取消造成的内存泄漏
这种Android的内存泄露比纯Java的内存泄漏还要严重,因为其他一些Android程序可能引用系统的Android程序的对象(比如注册机制)。即使Android程序已经结束了,但是别的应用程序仍然还有对Android程序的某个对象的引用,泄漏的内存依然不能被垃圾回收。
(2) 集合中对象没有关闭造成的内存泄漏
通常把一些对象的引用加入到了集合中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,慢慢地这个集合就会越来越大。如果这个集合是静态的话,那情况就会更严重。
1.2 资源对象没有关闭造成的内存泄漏
资源对象比如Cursor、File文件等往往都用了一些缓冲,在不使用的时候应该及时关闭它们,以便它们的缓冲及时回收内存。这些缓冲不仅存在于Java虚拟机内,还存在于Java虚拟机外,如果仅仅是把它的引用设置为空,而不关闭它们,那么往往会造成内存泄漏。
一些不良代码造成的内存压力原因如下:
◆ Bitmap没有调用recycle( );
◆ 构造Adapter时,没有使用缓存的convertView;
◆ ThreadLocal使用不当;
◆ 其他。
2 内存泄漏的检测及定位
2.1 内存泄漏的检测
Android应用程序是基于虚拟机的,其内存管理都是由Dalvik[2]代为管理,GC的回收不是及时的。一个正常的应用程序在其运行稳定后其内存的占用量是基本稳定的,不应该是无限制的增长。同样,对任何一个类的对象的使用个数也有一个相对稳定的上限,不应该是持续增长的。当我们持续地观察某个应用程序运行过程中使用内存的大小和各实例的个数时,如果内存的大小持续增长,则说明系统存在内存泄漏的问题;如果特定类的实例对象个数随时间而增长,则说明这个类的实例可能存在泄漏情况。比如一个Activity被关掉之后,其内存的引用对象会在下次GC回收[3]的时候通过回收算法计算,如果这部分内存已经属于可回收的对象,那么这些垃圾对象会被一并回收,内存未泄漏趋势图如图1所示。
图1 内存未泄漏趋势图
内存泄漏趋势图如图2所示。在重复打开关闭某个应用程序的时候,内存一直在向上爬升,也就是说每次关闭这个Activity的时候,有些应该释放的内存并没有被释放掉。由此我们可以确定这个应用程序存在着内存泄漏的问题。
图2 内存泄漏趋势图
2.2 内存泄漏的位置定位
查找内存泄漏一种比较彻底的方法就是代码走查,我们可以一行一行地分析对象的创建去留等等[4],但会很耗时间也比较迷茫。这里可以通过Eclipse Memory Analyzer Tool(MAT)工具来定位内存泄漏的位置,该方法只适用于Java层的查找,对C/C++没用,也就是说只针对于被虚拟机来管理的进程和内存。MAT的解析文件是.hprof文件,这个文件里面存放了某进程的内存快照,MAT通过解析.hprof文件就会自动生成一个内存泄漏推测报告,通过分析这个报告就可以准确定位到有可能存在内存泄漏的具体位置。
然而,还有一些内存泄漏通过MAT是查不出来的,比如native的代码,对C/C++是无能为力的,对于这些问题本文并没有做过多的研究。
在编写应用程序的过程中,对于BraodcastReceiver、ContentObserver、FileObserver在Activity onDestory或者某类声明周期结束之后一定要注销掉,否则这个Activity类会被系统强引用,不会被内存回收。
在定义成员变量时,不要直接对Activity进行引用而作为成员变量。如果不得不这么做,那么可以用private Weak Reference
在应用程序中,很多内存泄漏是由于循环引用而造成的,比如a中包含了b,b中包含了c,c中又包含a,这样只要一个对象存在,那么其他对象肯定会一直常驻内存。因此,在编写应用程序时要从逻辑上来分析是否需要这样的设计。
Bitmap对象不再使用时,调用recycle()方法释放内存。如果一个Bitmap对象比较占内存,当它不再被使用的时候,可以调用Bitmap.recycle()方法回收此对象的像素所占用的内存,这个不是必须的,可视情况而定。
还要注意释放对象的引用。当一个生命周期较短的对象A,被一个生命周期较长的对象B保有其引用的情况下,在A的生命周期结束时,要在B中清除掉对A的引用。
4 内存监测工具DDMS和内存分析工具MAT
4.1 内存监测工具DDMS
我们在开发Android应用程序时,很容易造成内存的泄漏,这时需要一些工具来帮助我们检查代码中是否存在会造成内存泄漏的不良代码。在Android tools的DDMS里面带有一个内存监测工具Heap,用它来监测应用程序使用内存的情况,这里需要和Eclipse配合使用。利用 Heap工具监测应用进程使用内存情况的方法如下:
① 首先启动Eclipse,切换到DDMS透视图,并确认Devices视图、Heap视图都已打开。将要测试的设备(比如手机)通过USB数据线连接到电脑上,连接成功后,会在DDMS的Devices视图界面中显示手机设备的序列号,以及设备中正在运行的部分进程信息。
评论