Android 内存溢出解决方案(OOM) 整理总结(转)-Android新手入门-eoe Android开发者社区_Android开发论坛

Android 4.0

  • 用户名
  • Email
登录 后使用快捷导航
没有帐号?免费加入
查看: 4982|回复: 50

Android 内存溢出解决方案(OOM) 整理总结(转)

    [复制链接]
1113567888 当前在线
积分
2938

升级   18.25%

当前用户组为 No.6 江湖开发者
当前积分为 2938, 升到下一级还需要 1962 点。

37

主题

806

帖子

1755

e币

No.6 江湖开发者

No.6 江湖开发者, 积分 2938, 距离下一级还需 1962 积分

No.6 江湖开发者, 积分 2938, 距离下一级还需 1962 积分
积分
2938
楼主
发表于 2012-9-12 23:07:24 | 只看该作者 |倒序浏览 |阅读模式

没有eoe的账号,级别还太低,出门如何吹牛逼?

您需要 登录 才可以下载或查看,没有帐号?免费加入

x
在最近做的工程中发现加载的图片太多或图片过大时经常出现OOM问题,找网上资料也提供了很多方法,但自己感觉有点乱,特此,今天在不同型号的三款安卓手机上做了测试,因为有效果也有结果,今天小马就做个详细的总结,以供朋友们共同交流学习,也供自己以后在解决OOM问题上有所提高,提前讲下,片幅有点长,涉及的东西太多,大家耐心看,肯定有收获的,里面的很多东西小马也是学习参考网络资料使用的,先来简单讲下下:
   一般我们大家在遇到内存问题的时候常用的方式网上也有相关资料,大体如下几种:
   一:在内存引用上做些处理,常用的有软引用、强化引用、弱引用
   二:在内存中加载图片时直接在内存中做处理,如:边界压缩
   三:动态回收内存
   四:优化Dalvik虚拟机的堆内存分配
   五:自定义堆内存大小
   可是真的有这么简单吗,就用以上方式就能解决OOM了?不是的,继续来看...
   下面小马就照着上面的次序来整理下解决的几种方式,数字序号与上面对应:
   1:软引用(SoftReference)、虚引用(PhantomRefrence)、弱引用(WeakReference),这三个类是对heap中java对象的应用,通过这个三个类可以和gc做简单的交互,除了这三个以外还有一个是最常用的强引用
    1.1:强引用,例如下面代码:
Object o=new Object();      
Object o1=o;  
     上面代码中第一句是在heap堆中创建新的Object对象通过o引用这个对象,第二句是通过o建立o1到new Object()这个heap堆中的对象的引用,这两个引用都是强引用.只要存在对heap中对象的引用,gc就不会收集该对象.如果通过如下代码:
o=null;      
o1=null
      heap中对象有强可及对象、软可及对象、弱可及对象、虚可及对象和不可到达对象。应用的强弱顺序是强、软、弱、和虚。对于对象是属于哪种可及的对象,由他的最强的引用决定。如下:
String abc=new String("abc");  //1      
SoftReference<String> abcSoftRef=new SoftReference<String>(abc);  //2      
WeakReference<String> abcWeakRef = new WeakReference<String>(abc); //3      
abc=null; //4      
abcSoftRef.clear();//5   
上面的代码中:
    第一行在heap对中创建内容为“abc”的对象,并建立abc到该对象的强引用,该对象是强可及的。第二行和第三行分别建立对heap中对象的软引用和弱引用,此时heap中的对象仍是强可及的。第四行之后heap中对象不再是强可及的,变成软可及的。同样第五行执行之后变成弱可及的。
        1.2:软引用
               软引用是主要用于内存敏感的高速缓存。在jvm报告内存不足之前会清除所有的软引用,这样以来gc就有可能收集软可及的对象,可能解决内存吃紧问题,避免内存溢出。什么时候会被收集取决于gc的算法和gc运行时可用内存的大小。当gc决定要收集软引用是执行以下过程,以上面的abcSoftRef为例:

    1 首先将abcSoftRef的referent设置为null,不再引用heap中的new String("abc")对象。
    2 将heap中的new String("abc")对象设置为可结束的(finalizable)。
    3 当heap中的new String("abc")对象的finalize()方法被运行而且该对象占用的内存被释放, abcSoftRef被添加到它的ReferenceQueue中。
   注:对ReferenceQueue软引用和弱引用可以有可无,但是虚引用必须有,参见:
Reference(T paramT, ReferenceQueue<? super T>paramReferenceQueue)
         被 Soft Reference 指到的对象,即使没有任何 Direct Reference,也不会被清除。一直要到 JVM 内存不足且 没有 Direct Reference 时才会清除,SoftReference 是用来设计 object-cache 之用的。如此一来 SoftReference 不但可以把对象 cache 起来,也不会造成内存不足的错误 (OutOfMemoryError)。我觉得 Soft Reference 也适合拿来实作 pooling 的技巧。
A obj = new A();   
Refenrence sr = new SoftReference(obj);   
  
//引用时   
if(sr!=null){   
    obj = sr.get();   
}else{   
    obj = new A();   
    sr = new SoftReference(obj);   
}   
    1.3:弱引用
                当gc碰到弱可及对象,并释放abcWeakRef的引用,收集该对象。但是gc可能需要对此运用才能找到该弱可及对象。通过如下代码可以了明了的看出它的作用:
String abc=new String("abc");      
WeakReference<String> abcWeakRef = new WeakReference<String>(abc);      
abc=null;      
System.out.println("before gc: "+abcWeakRef.get());      
System.gc();      
System.out.println("after gc: "+abcWeakRef.get());   
运行结果:   
before gc: abc   
after gc: null  
     gc收集弱可及对象的执行过程和软可及一样,只是gc不会根据内存情况来决定是不是收集该对象。如果你希望能随时取得某对象的信息,但又不想影响此对象的垃圾收集,那么你应该用 Weak Reference 来记住此对象,而不是用一般的 reference。  

A obj = new A();   
  
    WeakReference wr = new WeakReference(obj);   
  
    obj = null;   
  
    //等待一段时间,obj对象就会被垃圾回收  
  ...   
  
  if (wr.get()==null) {   
  System.out.println("obj 已经被清除了 ");   
  } else {   
  System.out.println("obj 尚未被清除,其信息是 "+obj.toString());  
  }  
  ...  
}  

    在此例中,透过 get() 可以取得此 Reference 的所指到的对象,如果返回值为 null 的话,代表此对象已经被清除。这类的技巧,在设计 Optimizer 或 Debugger 这类的程序时常会用到,因为这类程序需要取得某对象的信息,但是不可以 影响此对象的垃圾收集。

     1.4:虚引用

     就是没有的意思,建立虚引用之后通过get方法返回结果始终为null,通过源代码你会发现,虚引用通向会把引用的对象写进referent,只是get方法返回结果为null.先看一下和gc交互的过程在说一下他的作用.
      1.4.1 不把referent设置为null, 直接把heap中的new String("abc")对象设置为可结束的(finalizable).
      1.4.2 与软引用和弱引用不同, 先把PhantomRefrence对象添加到它的ReferenceQueue中.然后在释放虚可及的对象.
   你会发现在收集heap中的new String("abc")对象之前,你就可以做一些其他的事情.通过以下代码可以了解他的作用.

import java.lang.ref.PhantomReference;      
import java.lang.ref.Reference;      
import java.lang.ref.ReferenceQueue;      
import java.lang.reflect.Field;      
     
public class Test {      
    public static boolean isRun = true;      
     
    public static void main(String[] args) throws Exception {      
        String abc = new String("abc");      
        System.out.println(abc.getClass() + "@" + abc.hashCode());      
        final ReferenceQueue referenceQueue = new ReferenceQueue<String>();      
        new Thread() {      
            public void run() {      
                while (isRun) {      
                    Object o = referenceQueue.poll();      
                    if (o != null) {      
                        try {      
                            Field rereferent = Reference.class     
                                    .getDeclaredField("referent");      
                            rereferent.setAccessible(true);      
                            Object result = rereferent.get(o);      
                            System.out.println("gc will collect:"     
                                    + result.getClass() + "@"     
                                    + result.hashCode());      
                        } catch (Exception e) {      
     
                            e.printStackTrace();      
                        }      
                    }      
                }      
            }      
        }.start();      
        PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,      
                referenceQueue);      
        abc = null;      
        Thread.currentThread().sleep(3000);      
        System.gc();      
        Thread.currentThread().sleep(3000);      
        isRun = false;      
    }      
     
}


结果为
class java.lang.String@96354  
gc will collect:class java.lang.String@96354  好了,关于引用就讲到这,下面看2

   2:在内存中压缩小马做了下测试,对于少量不太大的图片这种方式可行,但太多而又大的图片小马用个笨的方式就是,先在内存中压缩,再用软引用避免OOM,两种方式代码如下,大家可参考下:
     方式一代码如下:
@SuppressWarnings("unused")
private Bitmap copressImage(String imgPath){
    File picture = new File(imgPath);
    Options bitmapFactoryOptions = new BitmapFactory.Options();
    //下面这个设置是将图片边界不可调节变为可调节
    bitmapFactoryOptions.inJustDecodeBounds = true;
    bitmapFactoryOptions.inSampleSize = 2;
    int outWidth  = bitmapFactoryOptions.outWidth;
    int outHeight = bitmapFactoryOptions.outHeight;
    bmap = BitmapFactory.decodeFile(picture.getAbsolutePath(),
         bitmapFactoryOptions);
    float imagew = 150;
    float imageh = 150;
    int yRatio = (int) Math.ceil(bitmapFactoryOptions.outHeight
            / imageh);
    int xRatio = (int) Math
            .ceil(bitmapFactoryOptions.outWidth / imagew);
    if (yRatio > 1 || xRatio > 1) {
        if (yRatio > xRatio) {
            bitmapFactoryOptions.inSampleSize = yRatio;
        } else {
            bitmapFactoryOptions.inSampleSize = xRatio;
        }

    }
    bitmapFactoryOptions.inJustDecodeBounds = false;
    bmap = BitmapFactory.decodeFile(picture.getAbsolutePath(),
            bitmapFactoryOptions);
    if(bmap != null){               
        //ivwCouponImage.setImageBitmap(bmap);
        return bmap;
    }
    return null;
}
     方式二代码如下:
package com.lvguo.scanstreet.activity;

import java.io.File;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.Toast;
import com.lvguo.scanstreet.R;
import com.lvguo.scanstreet.data.ApplicationData;
/**  
* @Title: PhotoScanActivity.java
* @Description: 照片预览控制类
* @author XiaoMa  
*/
public class PhotoScanActivity extends Activity {
    private Gallery gallery ;
    private List<String> ImageList;
    private List<String> it ;
    private ImageAdapter adapter ;
    private String path ;
    private String shopType;
    private HashMap<String, SoftReference<Bitmap>> imageCache = null;
    private Bitmap bitmap = null;
    private SoftReference<Bitmap> srf = null;
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.photoscan);
        Intent intent = this.getIntent();
        if(intent != null){
            if(intent.getBundleExtra("bundle") != null){
                Bundle bundle = intent.getBundleExtra("bundle");
                path = bundle.getString("path");
                shopType = bundle.getString("shopType");
            }
        }
        init();
    }
    private void init(){
        imageCache = new HashMap<String, SoftReference<Bitmap>>();
         gallery = (Gallery)findViewById(R.id.gallery);
         ImageList = getSD();
         if(ImageList.size() == 0){
            Toast.makeText(getApplicationContext(), "无照片,请返回拍照后再使用预览", Toast.LENGTH_SHORT).show();
            return ;
         }
         adapter = new ImageAdapter(this, ImageList);
         gallery.setAdapter(adapter);
         gallery.setOnItemLongClickListener(longlistener);
    }

评分

参与人数 1 +1 e币 +5 收起 理由
fengyiyezi + 1 + 5 很给力!

查看全部评分

本帖被以下淘专辑推荐:

  • · 其它|主题: 1, 订阅: 0
leo07172014 当前离线
积分
730

升级   5%

当前用户组为 No.4 中级开发者
当前积分为 730, 升到下一级还需要 570 点。

3

主题

68

帖子

50

e币

No.4 中级开发者

No.4 中级开发者, 积分 730, 距离下一级还需 570 积分

No.4 中级开发者, 积分 730, 距离下一级还需 570 积分
积分
730
推荐
发表于 2013-8-7 11:38:30 | 只看该作者
SoftReference<Bitmap> srf = imageCache.get(file.getName());
            Bitmap bit = srf.get();
            ImageView i = new ImageView(mContext);
            i.setImageBitmap(bit);
关于这段代码 想请教楼主:
Bitmap bit = srf.get(); 执行到这里时 Bitmap对象应该已经被强引用了吧?
在有强引用的状态下 不是对象绝对不会被回收吗?
那之前的SoftReference 软引用不是就没有作用了?
关于引用这里 不太明白 望指教
Jejay 当前在线
积分
196

升级   48%

当前用户组为 No.2 初级开发者
当前积分为 196, 升到下一级还需要 104 点。

3

主题

46

帖子

116

e币

No.2 初级开发者

No.2 初级开发者, 积分 196, 距离下一级还需 104 积分

No.2 初级开发者, 积分 196, 距离下一级还需 104 积分
积分
196
推荐
发表于 2013-8-7 14:29:55 | 只看该作者
看的晕乎乎的  可能是我水平有限  但还是  觉得  很不错
天天写代码 当前离线
积分
1143

升级   73.83%

当前用户组为 No.4 中级开发者
当前积分为 1143, 升到下一级还需要 157 点。

0

主题

305

帖子

1012

e币

No.4 中级开发者

No.4 中级开发者, 积分 1143, 距离下一级还需 157 积分

No.4 中级开发者, 积分 1143, 距离下一级还需 157 积分
积分
1143
推荐
发表于 2013-10-11 16:35:45 | 只看该作者
排版啊。。
楼主
重点不突出 无用的代码占了一大半
真正起作用的大概20行。。
1113567888 当前在线
积分
2938

升级   18.25%

当前用户组为 No.6 江湖开发者
当前积分为 2938, 升到下一级还需要 1962 点。

37

主题

806

帖子

1755

e币

No.6 江湖开发者

No.6 江湖开发者, 积分 2938, 距离下一级还需 1962 积分

No.6 江湖开发者, 积分 2938, 距离下一级还需 1962 积分
积分
2938
5
 楼主| 发表于 2012-9-12 23:08:13 | 只看该作者

   
    /**
     * Gallery长按事件操作实现
     */