zoukankan      html  css  js  c++  java
  • Android中图片过大造成内存溢出,OOM(OutOfMemory)异常解决方法

    当我们在做项目过程中,一遇到显示图片时,就要考虑图片的大小,所占内存的大小,原因就是Android分配给Bitmap的大小只有8M,试想想我们用手机拍照,普通的一张照片不也得1M以上,所以android处理图片时不得不考虑图片过大造成的内存异常。

       那时候只是简单地缓存图片到本地 然后将图片进行压缩,但是感觉这个问题没有很好的解决办法,只是减小了发生的几率

        这里,我将前辈们解决的方法重新整理一番,方便自己以后使用。

       1.在内存引用上做些处理,常用的有软引用、强化引用、弱引用

    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.在内存中加载图片时直接在内存中做处理
      A.边界压缩

    @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;//false --- allowing the caller to query the bitmap without having to allocate the memory for its pixels.
        bmap = BitmapFactory.decodeFile(picture.getAbsolutePath(),
                bitmapFactoryOptions);
        if(bmap != null){               
            //ivwCouponImage.setImageBitmap(bmap);
            return bmap;
        }
        return null;
    }
    

    B.边界压缩的情况下间接的使用了软引用来避免OOM

    /* 自定义Adapter中部分代码*/
            public View getView(int position, View convertView, ViewGroup parent) {
                File file = new File(it.get(position));
                SoftReference<Bitmap> srf = imageCache.get(file.getName());
                Bitmap bit = srf.get();
                ImageView i = new ImageView(mContext);
                i.setImageBitmap(bit);
                i.setScaleType(ImageView.ScaleType.FIT_XY);
                i.setLayoutParams( new Gallery.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
                        WindowManager.LayoutParams.WRAP_CONTENT));
                return i;
            }
    

    但大家都知道,这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存,如果图片多且大,这种方式还是会引用OOM异常的,因此需要进一步处理:
      A.第一种方式

    InputStream is = this.getResources().openRawResource(R.drawable.pic1);
         BitmapFactory.Options options=new BitmapFactory.Options();
         options.inJustDecodeBounds = false;
         options.inSampleSize = 10;   //width,hight设为原来的十分一
         Bitmap btp =BitmapFactory.decodeStream(is,null,options);
     if(!bmp.isRecycle() ){
             bmp.recycle()   //回收图片所占的内存
             system.gc()  //提醒系统及时回收
    }
    

     B.第二中方式

    /**
    * 以最省内存的方式读取本地资源的图片
    * */  
    public static Bitmap readBitMap(Context context, int resId){  
            BitmapFactory.Options opt = new BitmapFactory.Options();  
            opt.inPreferredConfig = Bitmap.Config.RGB_565;   
           opt.inPurgeable = true;  
           opt.inInputShareable = true;  
              //获取资源图片  
           InputStream is = context.getResources().openRawResource(resId);  
               return BitmapFactory.decodeStream(is,null,opt);  
       }
    

     C.在适当的时候垃圾回收

    if(bitmapObject.isRecycled()==false) //如果没有回收  
             bitmapObject.recycle
    

        D.优化Dalvik虚拟机的堆内存分配
        对于Android平台来说,其托管层使用的Dalvik JavaVM从目前的表现来看还有很多地方可以优化处理,eg我们在开发一些大型游戏或耗资源的应用中可能考虑手动干涉GC处理,使用 dalvik.system.VMRuntime类提供的setTargetHeapUtilization方法可以增强程序堆内存的处理效率。

    private final static floatTARGET_HEAP_UTILIZATION = 0.75f; 
    //在程序onCreate时就可以调用
    VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION);
    即可
    

     至于上面为何是0.75,是因为堆(HEAP)是VM中占用内存最多的部分,通常是动态分配的。堆的大小不是一成不变的,通常有一个分配机制来控制它的大小。比如初始的HEAP是4M大,当4M的空间被占用超过75%的时候,重新分配堆为8M大;当8M被占用超过75%,分配堆为16M大。倒过来,当16M的堆利用不足30%的时候,缩减它的大小为8M大。重新设置堆的大小,尤其是压缩,一般会涉及到内存的拷贝,所以变更堆的大小对效率有不良影响。


    E.自定义我们的应用需要多大的内存

    private final static int CWJ_HEAP_SIZE = 6* 1024* 1024 ;
     //设置最小heap内存为6MB大小
    VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE);
    
  • 相关阅读:
    Maven中配置生成单元测试报告配置
    maven-surefire-plugin总结
    Java程序内存分析:使用mat工具分析内存占用
    利用内存分析工具(Memory Analyzer Tool,MAT)分析java项目内存泄露
    JVM性能监控工具(一)-jdk命令行工具
    Java线程详细监控和其dump的分析使用—-分析Java性能瓶颈
    JDK自带内存及线程分析工具
    使用jstack和TDA进行java线程dump分析
    qt 插件开发
    精通linux设备驱动开发 笔记
  • 原文地址:https://www.cnblogs.com/childhooding/p/4345075.html
Copyright © 2011-2022 走看看