内存溢出OOM与内存泄漏ML

时间:2022-09-10 13:26:16

附, 微信团队原创分享:Android内存泄漏监控和优化技巧总结

一、如何避免OOM 异常

想要避免OOM 异常首先我们要知道什么情况下会导致OOM 异常。

1、图片过大导致OOM

Android 中用bitmap 时很容易内存溢出,比如报如下错误:Java.lang.OutOfMemoryError : bitmap size exceeds VM budget。
解决方法:
方法1: 等比例缩小图片
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
//Options 只保存图片尺寸大小,不保存图片到内存
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 2;
Bitmap bmp = null;
bmp = BitmapFactory.decodeResource(getResources(),
mImageIds[position],opts);
//回收
bmp.recycle();
以上代码可以优化内存溢出,但它只是改变图片大小,并不能彻底解决内存溢出。

方法2:对图片采用软引用,及时地进行recyle()操作
SoftReference<Bitmap> bitmap = new SoftReference<Bitmap>(pBitmap);
if(bitmap != null){
if(bitmap.get() != null && !bitmap.get().isRecycled()){
bitmap.get().recycle();
bitmap = null;
}
}

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

有时我们会手工的操作Bitmap 对象,如果一个Bitmap 对象比较占内存,当它不再被使用的时候,

可以调用Bitmap.recycle()方法回收此对象的像素所占用的内存,但这不是必须的,视情况而定。

3、资源对象没有关闭

资源性对象比如(Cursor,IO流等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们, 以便它们的缓冲及时回收内存。

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

4、硬件资源的释放
Android中最典型的需要注意释放硬件资源的情况是在Activity 的生命周期中,在onPause()、onStop()、onDestroy()方法中

需要适当的释放资源的情况,这些硬件资源可能包括:视频、音频、相机等。

5、构造Adapter 时,没有使用缓存的convertView
在使用ListView 的时候通常会使用Adapter,那么我们应该尽可能的使用ConvertView。

6、界面切换导致OOM

有时候我们会发现这样的问题,横竖屏切换N 次后OOM 了。
这种问题没有固定的解决方法,但是我们可以从以下几个方面下手分析。

(1)、看看页面布局当中有没有大的图片,比如背景图之类的。
去除xml 中相关设置,改在程序中设置背景图(放在onCreate()方法中):
Drawable drawable = getResources().getDrawable(R.drawable.id);
ImageView imageView = new ImageView(this);
imageView.setBackgroundDrawable(drawable);
在Activity destory 时注意,drawable.setCallback(null) 防止Activity 得不到及时的释放。

(2) 、跟上面方法相似, 直接把xml 配置文件加载成view 再放到一个容器里, 然后直接调用
this.setContentView(View view);方法,避免xml 的重复加载。

(3)、在页面切换时尽可能少地重复使用一些代码,比如:重复调用数据库,反复使用某些对象等等......

 7、避免内存泄漏导致OOM

内存泄漏会导致对象无法回收,从而占用系统内存,可能导致OOM,所以我们要尽量避免内存泄漏。

二、什么情况下会导致内存泄露(Memory  Leak)?

Android 的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的机器为24M。因此我们所能利用

的内存空间是有限的。如果我们的内存占用超过了一定的水平就会出现OutOfMemory 的错误。

内存溢出的几点原因:
1、资源释放问题
程序代码的问题,长期保持某些资源,如Context、Cursor、IO 流的引用,资源得不到释放造成内存泄露。

2、广播注册后没取消造成的内存泄漏

记得在activity销毁的时候一定要取消广播的注册

3、static 关键字的使用问题
static 是Java 中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。

所以用static 修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费过多的实例(Context 的情况最多),

这时就要谨慎对待了。

public class ClassName {
private static Context mContext;
//省略
}
以上的代码是很危险的,如果将Activity 赋值到mContext 的话。那么即使该Activity 已经onDestroy,

但是由于仍有对象保存它的引用,因此该Activity 依然不会被释放。

我们举Android 文档中的一个例子。

private static Drawable sBackground;
  @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);
}
sBackground 是一个静态的变量,但是我们发现,我们并没有显式的保存Contex 的引用,但是,当Drawable
与View 连接之后,Drawable 就将View 设置为一个回调,由于View 中是包含Context 的引用的,所以,实
际上我们依然保存了Context 的引用。这个引用链如下:Drawable->TextView->Context
所以,最终该Context 也没有得到释放,发生了内存泄露。
针对static 的解决方案
① 应该尽量避免static 成员变量引用资源耗费过多的实例,比如Context。
② 此时的Context 尽量使用ApplicationContext,因为Application 的Context 的生命周期比较长,引用它不会出现内存泄露的问题。
③ 使用WeakReference 代替强引用。比如可以使用WeakReference<Context> mContextRef;

4、线程导致内存溢出
线程产生内存泄露的主要原因在于线程生命周期的不可控。我们来考虑下面一段代码。

public class MyActivity extends Activity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  new MyThread().start();
  }
private class MyThread extends Thread{
  @Override
  public void run() {
  super.run();
  //do somthing
    }
  }
}
这段代码很平常也很简单,是我们经常使用的形式。我们思考一个问题:假设MyThread 的run 函数是一个很费
时的操作,当我们开启该线程后,将设备的横屏变为了竖屏,一般情况下当屏幕转换时会重新创建Activity,按照我
们的想法,老的Activity 应该会被销毁才对,然而事实上并非如此。
由于我们的线程是Activity 的内部类,所以MyThread 中保存了Activity 的一个引用,当MyThread 的run 函
数没有结束时,MyThread 是不会被销毁的,因此它所引用的老的Activity 也不会被销毁,因此就出现了内存泄露的问题。

有些人喜欢用Android 提供的AsyncTask,但事实上AsyncTask 的问题更加严重,Thread 只有在run 函数不结
束时才出现这种内存泄露问题,然而AsyncTask 内部的实现机制是运用了ThreadPoolExcutor,该类产生的Thread 对
象的生命周期是不确定的,是应用程序无法控制的,因此如果AsyncTask 作为Activity 的内部类,就更容易出现内存泄露的问题。
针对这种线程导致的内存泄露问题的解决方案:
第一、将线程的内部类,改为静态内部类(因为非静态内部类拥有外部类对象的强引用,而静态类则不拥有)。
第二、在线程内部采用弱引用保存Context 引用。

5、Handler内存泄漏
Handler作为内部类存在于Activity中,但是Handler生命周期与Activity生命周期往往并不是相同的,比如当Handler对象有Message在排队,则无法释放,进而导致本该释放的Acitivity也没有办法进行回收。 解决办法:

a,声明handler为static类,这样内部类就不再持有外部类的引用了,就不会阻塞Activity的释放

b,如果内部类实在需要用到外部类的对象,可在其内部声明一个弱引用引用外部类。

6、使用application的context来替代activity

这是一个很隐晦的内存泄漏的情况。有一种简单的方法来避免context相关的内存泄漏,最显著地一个是避免context逃出他自己的范围之外,使用Application context,这个context的生存周期和你的应用的生存周期一样长,而不是取决于activity的生存周期。如果你想保持一个长期生存的对象,并且这个对象需要一个context,记得使用application对象。

7、集合中对象没清理造成的内存泄漏

我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。

如果这个集合是static的话,那情况就更严重了。

内存溢出OOM与内存泄漏ML的更多相关文章

  1. 内存溢出&lpar;Oom&rpar;和内存泄露&lpar;Memory leak&rpar;

    1.概念 内存溢出(Oom):1.内存不够用:2.数据长度短的数据类型存储了一个数据长度较大的数据类型:3.一个结果 内存泄露(Memory leak):1.忘记释放已用内存,内存管理较为常见的现象: ...

  2. JVM:内存溢出OOM

    JVM:内存溢出OOM 本笔记是根据bilibili上 尚硅谷 的课程 Java大厂面试题第二季 而做的笔记 经典错误 JVM 中常见的两个 OOM 错误 *Error:栈溢出 ...

  3. java常见内存溢出&lpar;OOM&rpar;

    jvm内存区域 程序计数器一块很小的内存空间,作用是当前线程所执行的字节码的行号指示器. java栈与程序计数器一样,java栈(虚拟机栈)也是线程私有的,其生命周期与线程相同.通常存放基本数据类型, ...

  4. 内存溢出OOM

    如何避免OOM 异常? 想要避免OOM 异常首先我们要知道什么情况下会导致OOM 异常. 1.图片过大导致OOM Android 中用bitmap 时很容易内存溢出,比如报如下错误:Java.lang ...

  5. android 内存溢出oom错误的一些小见解

    转:http://blog.csdn.net/xuhui_7810/article/details/9493681 我们在代码里调用setBackgroundResource(int resid)来设 ...

  6. bitmap 内存溢出OOM的解决办法分享

    昨天遇到这个问题就是从一个输入流里调用BitmapFactory.decodeStream(this.getContentResolver().openInputStream(uri))得到一个bit ...

  7. 内存溢出之Tomcat内存配置

    设置Tomcat启动的初始内存其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4. 可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置 三.实例,以下给 ...

  8. JVM内存区域以及各区域的内存溢出异常,内存分代策略,垃圾收集算法,各种垃圾收集器

    本文整理自周志明老师的<深入理解Java虚拟机-JVM高级特性与最佳实践>第3版的第二章和第三章. 加上了一些网上拼拼凑凑的图片,个人认为很多博客复制来复制去,最后的东西都看不懂,所以从书 ...

  9. viewPager--viewpager时,发生内存溢出OOM问题

    两个问题:1.如果图片达到500kb每张,你这个划屏会有顿卡:2.快速滑动有出现0.几秒的白屏.图片越大,顿卡越明显. 回复parcool:500kb的背景算大的了,如果是想做图片墙,viewpage ...

随机推荐

  1. coalesce函数用法

    COALESCE函数会依次检查输入的参数,返回第一个不是NULL的参数,只有当传入COALESCE函数的所有的参数都是NULL的时候,函数才会返回NULL

  2. java 打开txt文件或者bat文件

    package open_exe; public class OpenExe { public static void openWinExe() { Runtime rn = Runtime.getR ...

  3. jboss入门学习1

    环境准备 win8 jdk1.6 jboss4.0.5 oracle10g 修改jboss默认端口 server/default/conf/jboss-service.xml 1.WebService ...

  4. centos mail 不能发邮件

    http://alfred-long.iteye.com/blog/1836488 (参考) 最近centos 6.4突然不能发邮件了, 直接用 mail命令测试也不收不到邮件 以下参考大侠们的经验后 ...

  5. VS2010调试时候未响应

    这几天使用vs2010,调试时候经常未响应,等了半天才缓过来,严重影响心情,决定解决这个问题. 搜寻一番,试着关闭VS,重新设置了vs2010的环境(在vs2010命令提示符下,执行devenv.ex ...

  6. 【Android】listview优化

    http://www.cnblogs.com/over140/archive/2011/03/23/1991100.html http://blog.sina.com.cn/s/blog_5fc933 ...

  7. linux安装php遇到的问题

    第一个问题:configure: error: libjpeg.(a|so) not found 很多人都纳闷儿了,我都安装了的,为什么就找不到呢?找不到其实就应试想到它的搜索位置里面是没有的,要不然 ...

  8. 【转载】Android开源:数据库ORM框架GreenDao学习心得及使用总结

    转载链接:http://www.it165.net/pro/html/201401/9026.html 最近在对开发项目的性能进行优化.由于项目里涉及了大量的缓存处理和数据库运用,需要对数据库进行频繁 ...

  9. 最长递增子序列&lpar;Longest Increase Subsequence&rpar;

    问题 给定一个长度为N的数组,找出一个最长的单调自增子序列(不一定连续,但是顺序不能乱).例如:给定一个长度为6的数组A{5, 6, 7, 1, 2, 8},则其最长的单调递增子序列为{5,6,7,8 ...

  10. TCP和UDP的&quot&semi;保护消息边界”

    转自:http://blog.csdn.net/zhangxinrun/article/details/6721427 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP ...