避免内存泄漏的方法

内存泄露概述

 在计算机科学中,内存泄漏(memoryleak)指由于疏忽或错误造成 程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了 对该段内存的控制,因而造成了内存的浪费

一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中 分配的,使用完后必须显式释放的内存。

内存泄漏分类

常发性内存泄漏

偶发性内存泄漏

一次性内存泄漏

隐式内存泄漏

 


Android常见内存泄露现象


1.查询数据库没有关闭游标

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

if (cursor.moveToNext()) {

... ...

}

修正示例代码:

Cursor cursor = null;

try {

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

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

... ...

}

} finally {

if (cursor != null) {

try {

cursor.close();

} catch (Exception e) {

//ignore this

}

}

}


2.构造Adapter时,没有使用缓存的convertView

 public View getView(int position, View convertView,ViewGroup parent) {

 View view = newXxx(...);

... .

return view;

}

修正示例代码:

public View getView(int position, View convertView,ViewGroup parent) {

View view = null;

 if (convertView !=null) {

 view = convertView;

populate(view, getItem(position));

 ...

} else {

 view = newXxx(...);

...

}

return view;

}

 

3. 错用静态对象

 private static DrawablesBackground;

 @Override

protected void onCreate(Bundle state) {

 super.onCreate(state);

TextView label = new TextView(this);

label.setText("Leaks are bad");

if (sBackground == null) {

sBackground = getDrawable(R.drawable.large_bitmap);

}

label.setBackgroundDrawable(sBackground);

setContentView(label);

}

  

4. 其它

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

• 没有释放对象的引用

• 没有在恰当的Activity 的生命周期时间点释放资源 …


解决措施总结:

1  释放已注册的BraodcastReceiver,ContentObserver,FileObserver等

2 不要直接对Activity进行直接引用作为成员变量

3 避免循环引用

4 谨慎使用static对象

5 使用WeakReference


内存分析工具

 内存分析工具-- Memory Analyzer Tool(MAT)

1 内存监测工具 -- DDMS

2 进程内存查看-- Procrank

 

MAT

MAT 是一个Eclipse插件,同时也有单独的RCP 客户端。

MAT介绍和详细的使用教程请参见:www.eclipse.org/mat

MAT分析过程:

1 生成.hprof 文件、

2 打开MAT 并导入.hprof文件

3 使用MAT 的视图工具分析内存

 

详解如何使用MAT的视图工具

• 导入.hprof 文件,点击Dominator Tree

• 按Package分组,选择自己所定义的Package 类点右键,在弹出菜单中 选择List objects->With incoming references

• 右键点击某一项可疑类,,并选择Path to GC Roots -> exclude weak/softreferences, 会进一步筛选出跟程序相关的所有有内存泄露的类。

• 据此,可以追踪到代码中的某一个产生泄露的类.


进程查看

adb shell dumpsys meminfo com.android.xxx


如何避免内存泄漏汇总

1)文件、数据库和网络操作

对文件、数据库和网络进行操作时,使用完后必须进行显示的关闭操作,释放资源。例如,在数据库操作完后,使用try … finally…确保关闭游标。

Cursor cursor= null;

try {

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

} finally {

  if (cursor != null) {

      try{

          cursor.close();

      } catch (Exception e) {

          }

      }

}

这些文件、数据库和网络连接,除非其显式的关闭了连接,否则是不会自动被GC 回收,造成资源的泄露。

2)利用ListView、GridView的缓存

构造ListView、GridView等控件的Adapter时,需要利用缓存的View,减少内存占用,提高刷新效率。public View getView(int position, View convertView, ViewGroupparent) 是ListView提供每一个item所需要的view对象,可以利用缓存的convertView,而不用每次都生成一个新的View对象。例如:

public ViewgetView(int position, View convertView, ViewGroup parent) {

       Viewview = null;

        if(convertView != null)

             view = convertView;

        populate(view,getItem(position));

        ...

       }else {

        view= new Xxx(...); 

         ...

        }

       returnview;

 }

    对于ListView、GridView中含有很多子项时,必须这样操作,否则每次都生成新的View,将耗用大量的内存。

3)使用StringBuilder类

使用合理的方式产生String对象,String类型是程序中最常用的,如果要经常对String对象的内容进行操作,推荐使用StringBuilder类这种字符串对象。例如:

String str1 =“ab” + “cd” + “ef” + “gh”;

StringBuilderbuilder = new StringBuilder ();

String str2 =builder.append(“ab”).append(“cd”).append(“ef”).append(“gh”).toString;

第一种为字符串str1赋值时要产生很多临时的字符串对象,而第二种StringBuilder是可变的字符串对象,只需生成一个对象,因此第二种方式比第一种方式占用的内存更少。

4)合理使用静态变量

静态变量是全局的,其生命周期和它所在的类是一致的,除非所在的类被释放,否则GC是不会回收该静态变量的。例如:

public class ClassName {  

private static Context context;  

}  

以上的代码是很危险的,如果将Activity赋值到context的话,那么即使该Activity已经onDestroy,但是由于仍有对象持有它的引用,因此该Activity依然不会被释放。所以,应该尽量避免static成员变量引用资源耗费过多的实例。

5)  Context的引用

Context尽量使用Application Context,因为这种Context 的生存周期和该应用的生存周期一样长,而不是取决于activity 的生存周期。如果你想保持一个长期生存的对象,并且这个对象需要一个Context,那么可以使用Application 这个对象。Application Context可以通过Context.getApplicationContext()或Activity.getApplication()来获取。

6) Activity中注意释放资源

Android应用程序中,在onPause()、onStop()、onDestroy()等生命周期中,需要注意相关资源的释放,保证无用的资源能够及时的回收。

7) 注册监听器与注销监听器必须成对出现

动态注册的数据库变化监听器、广播接收器等,在不使用后必须注销监听器。

8) 容器的使用

容器中的对象,不使用后,要从容器中移除,防止该对象一直被容器引用,导致无法释放。

9) 尽早释放无用对象的引用

尽量使用临时变量,让引用变量在方法退出后,自动设置为null,暗示垃圾收集器来收集该对象,防止发生内存泄露。也可强制设置为空,垃圾回收会将值为null的对象作为垃圾,提高GC回收机制效率。

10)  谨慎处理对象引用

GC不能回收对象占用内存的原因就在于该对象有被引用到,在引用对象时要谨慎,要避免循环引用。另外,当一个生命周期较短的对象A,被一个生命周期较长的对象B保有其引用的情况下,在A的生命周期结束时,要在B中清除掉对A的引用。

11)  不要在经常调用的方法中创建对象

不要在循环中创建对象,适当使用Hashtable,Vector创建一组对象容器,然后从容器中去取那些对象,而不用每次new之后又丢弃。

12) Bitmap的回收

Bitmap是内存占用大户,特别是分辨率大的图片,如果要显示多张那问题就更显著了。可以说大部分的OutOfMemory,都是因为Bitmap的问题。在用完Bitmap时,要及时调用的recycle(),最好再显示的置空,以便GC能够及时回收。

Private BitmapmBitmap = Bitmap.createBitmap(480, 960, Bitmap.Config.ARGB_8888);

if (mBitmap !=null && !mBitmap.isRecycled()) {

  mBitmap.recycle();

  mBitmap = null;

}

13) 图片资源的使用

使用图片资源时,要从源头上控制图片资源的大小,尽量使用png、9.png格式的图片,减小内存占用。

14) 避免使用AnimationDrawable

AnimationDrawable这种方式很耗内存,可以改用Handle发送消息给ImageView循环设置背景图片的方式来实现同样的动画效果,避免占用过多的内存。

15)消除冗余资源

    过多的资源也会导致应用占用的内存增大,在开发时,很有必要消除那些无用的、冗余的资源文件,其中也包括冗余的代码,为APK瘦身。

16) 软引用、弱引用

对于占用内存大的对象,可以采用软引用或是弱引用的方式,软引用的对象在内存不足时释放,弱引用的对象在GC运行时释放。

A a = new A();

SoftRefenrencesr = new SoftReference(a);

B b = newB();  

WeakReferencewr = new WeakReference(b); 

17) 确保多分支或多出口时的资源回收

在函数中有多个分支或者多个出口时,应保证在每个分支或每个出口处都能执行到资源回收的代码,避免在某些情况下,漏掉了资源回收。

18) 大型文件或数据库一次取太多的数据导致内存溢出

开启大型文件或数据库一次取太多的数据,造成 Out OfMemory Error 的状况,需要大概计算数据量的最大值,并设定所需最小及最大的内存空间值。

19) Handle的remove操作

在有Handle处理的应用中要注意,在应用退出时要调用Handle的removeCallbacks(mRunnable)方法进行remove操作,其中mRunnableRunnable的接口对象,并且该mRunnable对象要置空,否则会导致嵌套引用。

20)避免窗口泄露

在销毁Activity时要确保先销毁该Activity所有的附属窗口,避免Activity关闭后,其附属窗口没有关闭而造成的窗口泄露。

21) 进程自杀

由于Android系统的内存管理策略,应用程序在退出后,其进程并不会立即退出,而是要等到系统内存不足时才被结束掉。所以必要时,某些应用程序退出后可以采用自杀的方式结束该进程,即调用Process.killProcess(Process.myPid()),来节省内存空间。一般应用不需要自杀处理。

22)映射文件mmap

关闭映射内存(munmap)时,其指定的大小应该和映射文件(mmap)映射到内存大小保持一致,避免映射到共享内存对象得不到释放,而产生内存泄露。

23)Native层的使用规范

对于用C/C++开发的Native层的代码,在内存使用时,也要遵循相应的规范:malloc和free,new和delete必须配对使用,即内存分配后必须手动回收,避免内存泄露;对于刚分配到的内存,一定要进行初始化操作后才能使用;另外,要杜绝释放正在使用的内存,避免指针悬挂,同时在free或delete释放了内存之后,应将指针设置为NULL,防止产生“野指针”。

24)JNI层的使用规范

在 JNI 编程时,对于创建过多的 Local Reference,对被引用的 Java 对象操作结束后,需要调用 JNI 函数DeleteLocalRef(),及时将Local Reference删除;在使用 Global reference 时,当 native code 不再需要访问 Global reference 时,也应当调用DeleteGlobalRef() 删除 Global reference 和它引用的 Java 对象,避免内存泄露。

 


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值