• A+

OOM 内存溢出的原因和处理方法

1 OOM的可能原因?

数据库的cursor没有及时关闭
构造Adapter没有使用缓存contentview
RegisterReceiver()与unRegisterReceiver()成对出现
未关闭InputStream outputStream
Bitmap 使用后未调用recycle()
static等关键字
非静态内部类持有外部类的引用 context泄露
2 处理oom的优化方法

1.针对数据库cursor没有关闭的情况,如果我们查询数据库得到的数据量比较小的话是不会造成内存溢出的,但是如果太大的话就容易发生这种异常,所以当我们在使用完Cursor的时候就应该手动调用它的close方法关闭cursor. 

2.针对adapter没有复用convertView的情况,在我们开发的过程中,经常会遇到一个listview加载成百上千条的数据,如果不复用convertView的话就会在每次执行getView方法的时候都创建一个新的对象,而这个方法的调用速度又是很快的,java不能够及时的回收内存就会造成OOM异常,这时候除了要在getView方法里面对convertView进行判断后复用,还应该使用ViewHolder类来保存通过过findViewById得到的子控件地址值. 

3.在activity中注册了广播,但是在activity退出的时候没有取消注册的话可能会造成内存溢出,需要手动的在相应的位置进行反注册.

4.不关闭输入输出流的话就相当于在内存和硬盘一直存在着连接占用着资源,当其他操作需要资源时就会造成内存溢出.

5.位图在安卓中占用的内存是很大的,使用后如果不及时回收的话会占用大量空间,所以针对位图的操作一般有如下解决方案:

1)及时的调用resycle方法来手动的回收;

2)设置采样率,有时候我们不一定要把图片完全显示出来,这时候就要按比例来缩放,在我们得到采样率的时候就可以将图片缩小后再进行加载,节省大量的内存;

3)使用软引用. 

6.static关键字

开发中使用关键字static可以将成员变量和方法变成类变量和类方法,这样会大大延长变量的生命周期,如果我们过多的使用static来保存占用资源过多的对象的引用就会造成内存溢出,比如用static修饰一个上下文的对象的话.

第一,应该尽量避免static成员变量引用资源耗费过多的实例,比如Context。

第二、Context尽量使用Application Context,因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。

第三、使用WeakReference代替强引用。比如可以使用WeakReference<Context> mContextRef;

7.非静态类内部类 上下文泄露

例如在activity中使用了线程内部类,这时候在线程内存就会保存一个activity的引用,当activity被销毁的时候,线程仍在执行的话,GC发现它还被其内部类的对象引用,所以activity就不会被回收,假设activity中还有大量的图片资源就容易造成oom,处理方法就是使用静态内部类,不持有外部引用,从而不影响GC对外部类的回收。

三、oom 是否可以try catch ?参考 https://blog.csdn.net/sinat_29912455/article/details/51125748 

一般不适合这么处理 

Java中管理内存除了显式地catch OOM之外还有更多有效的方法:比如SoftReference, WeakReference, 硬盘缓存等。

在JVM用光内存之前,会多次触发GC,这些GC会降低程序运行的效率。
如果OOM的原因不是try语句中的对象(比如内存泄漏),那么在catch语句中会继续抛出OOM

四、bitmap如何处理大图,如何预防OOM?

BitmapFactory类提供了几种方法,可以通过一定的采样率对bitmap进行比例加载,获取所需的大小的位图

mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
//通过解码获取采样率 并通过采样率获取相应的图片
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {

// 第一步inJustDecodeBounds=true 检查尺寸大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);

// 计算样本尺寸
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

// 进行采样
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}

public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// 原始图片大小
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {

final int halfHeight = height / 2;
final int halfWidth = width / 2;

//计算最大的压缩比例值都是2的幂次方,而且宽和高大于被要求的宽和高。
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
BitmapFactory 还提供了解码文件、资源、字符数组、输入流等形式的解码方法

五、位图的软引用方式 减小内存占用以及避免内存溢出oom 和应用的crash

Java对引用的分类有 Strongreference, SoftReference, WeakReference, PhatomReference 四种。

在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。

软/弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。利用这个队列可以得知被回收的软/弱引用的对象列表,从而为缓冲器清除已失效的软/弱引用。

假设我们的应用会用到大量的默认图片,比如应用中有默认的头像,默认游戏图标等等,这些图片很多地方会用到。如果每次都去读取图片,由于读取文件需要硬件操作,速度较慢,会导致性能较低。所以我们考虑将图片缓存起来,需要的时候直接从内存中读取。但是,由于图片占用内存空间比较大,缓存很多图片需要很多的内存,就可能比较容易发生OutOfMemory异常。这时,我们可以考虑使用软/弱引用技术来避免这个问题发生。以下就是高速缓冲器的雏形:

首先定义一个HashMap,用于保存软引用对象,再来定义一个方法,保存Bitmap的软引用到这个HashMap中。

使用软引用以后,在OutOfMemory异常发生之前,这些缓存的图片资源的内存空间可以被释放掉的,从而避免内存达到上限,避免Crash发生。

如果只是想避免OutOfMemory异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。

另外可以根据对象是否经常使用来判断选择软引用还是弱引用。如果该对象可能会经常使用的,就尽量用软引用。如果该对象不被使用的可能性更大些,就可以用弱引用。

 

所属分类:架构

全部评论: 0

    我有话说:
    ×