各位兄弟姐妹,Java开发中的内存泄露的问题经常会给我们带来很多烦恼。特别是对一些新手,如果平时不注意一些细节问题,最后很可能会导致很严重的后果。

在Android中的Java开发也同样会有这样的问题。附件中的pdf整理了一些关于Android中的Java开发,在内存使用方面需要注意的一些问题,希望能够对大家有所帮助。

接下篇: [Android] 内存泄漏调试经验分享 (二) http://rayleeya.iteye.com/blog/755657

Android内存泄漏调试

Madeby李文栋

liwd@thunderst.com|rayleeya@gmail.com

2010-07-25Friday

一、概述 1

二、Android(Java)中常见的容易引起内存泄漏的不良代码 1

()查询数据库没有关闭游标 2

()构造Adapter时,没有使用缓存的convertView 3

()Bitmap对象不在使用时调用recycle()释放内存 4

()释放对象的引用 4

()其他 5

三、内存监测工具DDMS-->Heap 5

四、内存分析工具MAT(MemoryAnalyzerTool) 7

()生成.hprof文件 7

()使用MAT导入.hprof文件 8

()使用MAT的视图工具分析内存 8

一、概述

Java编程中经常容易被忽视,但本身又十分重要的一个问题就是内存使用的问题。Android应用主要使用Java语言编写,因此这个问题也同样会在Android开发中出现。本文不对Java编程问题做探讨,而是对于在Android中,特别是应用开发中的此类问题进行整理。

由于作者接触Android时间并不是很长,因此如有叙述不当之处,欢迎指正。

二、Android(Java)中常见的容易引起内存泄漏的不良代码

Android主要应用在嵌入式设备当中,而嵌入式设备由于一些众所周知的条件限制,通常都不会有很高的配置,特别是内存是比较有限的。如果我们编写的代码当中有太多的对内存使用不当的地方,难免会使得我们的设备运行缓慢,甚至是死机。为了能够使得Android应用程序安全且快速的运行,Android的每个应用程序都会使用一个专有的Dalvik虚拟机实例来运行,它是由Zygote服务进程孵化出来的,也就是说每个应用程序都是在属于自己的进程中运行的。一方面,如果程序在运行过程中出现了内存泄漏的问题,仅仅会使得自己的进程被kill掉,而不会影响其他进程(如果是system_process等系统进程出问题的话,则会引起系统重启)。另一方面Android为不同类型的进程分配了不同的内存使用上限,如果应用进程使用的内存超过了这个上限,则会被系统视为内存泄漏,从而被kill掉。Android为应用进程分配的内存上限如下所示:

位置:/ANDROID_SOURCE/system/core/rootdir/init.rc部分脚本

#Definetheoom_adjvaluesfortheclassesofprocessesthatcanbe

#killedbythekernel.TheseareusedinActivityManagerService.

setpropro.FOREGROUND_APP_ADJ0

setpropro.VISIBLE_APP_ADJ1

setpropro.SECONDARY_SERVER_ADJ2

setpropro.BACKUP_APP_ADJ2

setpropro.HOME_APP_ADJ4

setpropro.HIDDEN_APP_MIN_ADJ7

setpropro.CONTENT_PROVIDER_ADJ14

setpropro.EMPTY_APP_ADJ15

#Definethememorythresholdsatwhichtheaboveprocessclasseswill

#bekilled.Thesenumbersareinpages(4k).

setpropro.FOREGROUND_APP_MEM1536

setpropro.VISIBLE_APP_MEM2048

setpropro.SECONDARY_SERVER_MEM4096

setpropro.BACKUP_APP_MEM4096

setpropro.HOME_APP_MEM4096

setpropro.HIDDEN_APP_MEM5120

setpropro.CONTENT_PROVIDER_MEM5632

setpropro.EMPTY_APP_MEM6144

#Writevaluemustbeconsistentwiththeaboveproperties.

#Notethatthedriveronlysupports6slots,sowehaveHOME_APPatthe

#samememorylevelasservices.

write/sys/module/lowmemorykiller/parameters/adj0,1,2,7,14,15

write/proc/sys/vm/overcommit_memory1

write/proc/sys/vm/min_free_order_shift4

write/sys/module/lowmemorykiller/parameters/minfree1536,2048,4096,5120,5632,6144

#Setinititsforkedchildren'soom_adj.

write/proc/1/oom_adj-16

正因为我们的应用程序能够使用的内存有限,所以在编写代码的时候需要特别注意内存使用问题。如下是一些常见的内存使用不当的情况。

()查询数据库没有关闭游标

描述:

程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor后没有关闭的情况。如果我们的查询结果集比较小,对内存的消耗不容易被发现,只有在常时间大量操作的情况下才会复现内存问题,这样就会给以后的测试和问题排查带来困难和风险。

示例代码:

Cursorcursor=getContentResolver().query(uri...);

if(cursor.moveToNext()){

......

}

修正示例代码:

Cursorcursor=null;

try{

cursor=getContentResolver().query(uri...);

if(cursor!=null&&cursor.moveToNext()){

......

}

}finally{

if(cursor!=null){

try{

cursor.close();

}catch(Exceptione){

//ignorethis

}

}

}

()构造Adapter时,没有使用缓存的convertView

描述:

以构造ListViewBaseAdapter为例,在BaseAdapter中提高了方法:

publicViewgetView(intposition,ViewconvertView,ViewGroupparent)

来向ListView提供每一个item所需要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上面的listitemview对象会被回收,然后被用来构造新出现的最下面的listitem。这个构造过程就是由getView()方法完成的,getView()的第二个形参ViewconvertView就是被缓存起来的listitemview对象(初始化时缓存中没有view对象则convertViewnull)

由此可以看出,如果我们不去使用convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费资源也浪费时间,也会使得内存占用越来越大。ListView回收listitemview对象的过程可以查看:

android.widget.AbsListView.java-->voidaddScrapView(Viewscrap)方法。

示例代码:

publicViewgetView(intposition,ViewconvertView,ViewGroupparent){

Viewview=newXxx(...);

......

returnview;

}

修正示例代码:

publicViewgetView(intposition,ViewconvertView,ViewGroupparent){

Viewview=null;

if(convertView!=null){

view=convertView;

populate(view,getItem(position));

...

}else{

view=newXxx(...);

...

}

returnview;

}

()Bitmap对象不在使用时调用recycle()释放内存

描述:

有时我们会手工的操作Bitmap对象,如果一个Bitmap对象比较占内存,当它不在被使用的时候,可以调用Bitmap.recycle()方法回收此对象的像素所占用的内存,但这不是必须的,视情况而定。可以看一下代码中的注释:

/**

*Freeupthememoryassociatedwiththisbitmap'spixels,andmarkthe

*bitmapas"dead",meaningitwillthrowanexceptionifgetPixels()or

*setPixels()iscalled,andwilldrawnothing.Thisoperationcannotbe

*reversed,soitshouldonlybecalledifyouaresurethereareno

*furtherusesforthebitmap.Thisisanadvancedcall,andnormallyneed

*notbecalled,sincethenormalGCprocesswillfreeupthismemorywhen

*therearenomorereferencestothisbitmap.

*/

()释放对象的引用

描述:

这种情况描述起来比较麻烦,举两个例子进行说明。

示例A

假设有如下操作

publicclassDemoActivityextendsActivity{

......

privateHandlermHandler=...

privateObjectobj;

publicvoidoperation(){

obj=initObj();

...

[Mark]

mHandler.post(newRunnable(){

publicvoidrun(){

useObj(obj);

}

});

}

}

我们有一个成员变量obj,在operation()中我们希望能够将处理obj实例的操作post到某个线程的MessageQueue中。在以上的代码中,即便是mHandler所在的线程使用完了obj所引用的对象,但这个对象仍然不会被垃圾回收掉,因为DemoActivity.obj还保有这个对象的引用。所以如果在DemoActivity中不再使用这个对象了,可以在[Mark]的位置释放对象的引用,而代码可以修改为:

......

publicvoidoperation(){

obj=initObj();

...

finalObjecto=obj;

obj=null;

mHandler.post(newRunnable(){

publicvoidrun(){

useObj(o);

}

}

}

......

示例B:

假设我们希望在锁屏界面(LockScreen)中,监听系统中的电话服务以获取一些信息(如信号强度等),则可以在LockScreen中定义一个PhoneStateListener的对象,同时将它注册到TelephonyManager服务中。对于LockScreen对象,当需要显示锁屏界面的时候就会创建一个LockScreen对象,而当锁屏界面消失的时候LockScreen对象就会被释放掉。

但是如果在释放LockScreen对象的时候忘记取消我们之前注册的PhoneStateListener对象,则会导致LockScreen无法被垃圾回收。如果不断的使锁屏界面显示和消失,则最终会由于大量的LockScreen对象没有办法被回收而引起OutOfMemory,使得system_process进程挂掉。

总之当一个生命周期较短的对象A,被一个生命周期较长的对象B保有其引用的情况下,在A的生命周期结束时,要在B中清除掉对A的引用。

()其他

Android应用程序中最典型的需要注意释放资源的情况是在Activity的生命周期中,在onPause()onStop()onDestroy()方法中需要适当的释放资源的情况。由于此情况很基础,在此不详细说明,具体可以查看官方文档对Activity生命周期的介绍,以明确何时应该释放哪些资源。

更多相关文章

  1. Android系统的Binder机制之一——Service Manager
  2. 如何使用Jdbc和Servlet操作Mysql数据库,编写Android登录注册服务
  3. Android复合文本SpannableString使用总结
  4. Android(安卓)4.4 meminfo 实现分析
  5. Android(安卓)实习生面试经历记录
  6. 箭头函数的基础使用
  7. 类和 Json对象
  8. NPM 和webpack 的基础使用
  9. Python list sort方法的具体使用

随机推荐

  1. android 获取屏幕是否被锁
  2. OkhttpRequest
  3. Android(安卓)基于surfaceView绘制正弦曲
  4. Android(安卓)照片墙功能实现
  5. android abslistview gridview介绍
  6. Android(安卓)使用Sharedpreference共享
  7. android中解析xml文件的工具类XmlUtils
  8. 【Android】onLongClick for TextView fi
  9. android 编译错误解决方法(一)
  10. Android(安卓)KEYCODE