关于安卓内存溢出问题探讨

来源:互联网 发布:java web生产验证吗 编辑:程序博客网 时间:2024/05/22 09:04
对于安卓的内存溢出,我是找不到什么好的方法,曾经疯狂的百度过,看的文章都是大同小异,很多都是浮云,下面是我以前总结的,前些天有人问到,我自己也没解决,我翻到了自己的笔记,就是下面的,从word粘上来,大家探讨,看谁有其他解决方案可以交流下,下面是我的笔记:
大家都知道Android的上层应用是基于 Dalvik Virtual Machine的。Dalvik VM的特点是基于寄存器,相比SUN的JVM(基于堆栈,没有寄存器)来说,理论上完成同样的功能需要的指令条数少,但是指令集复杂。到了Android2.2,Dalvik终于实现了JIT(Just In Time)功能,前进了一大步。
近期
们遇到OutOfMemory的错误,通常是堆内存溢出。网上有些帖子说可以通过函数设置应用的HEAP SIZE:
    private final static int CWJ_HEAP_SIZE = 6* 1024* 1024 ;
    VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE); //
设置最小heap内存为6MB大小。

    用这个方法解决问题,其实是不对的。 

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

上面只是个例子,不过可以看到三个参数:max heap size, min heap size, heap utilization(堆利用率)。Max Heap Size,是堆内存的上限值,Android的缺省值是16M(某些机型是24M),对于普通应用这是不能改的。函数setMinimumHeapSize其实只是改变了堆的下限值,它可以防止过于频繁的堆内存分配,当设置最小堆内存大小超过上限值时仍然采用堆的上限值(16M),对于内存不足没什么作用。所以说堆内存的大小无法改变,也就无法阻止内存溢出,你通过VMRuntime.getRuntime()setMinimumHeapSize只是改变了堆的下限值而已,而且VMRuntime类在android
2.3系统及以上系统已经没有了。

经过我多次测试,设置利用率大小是启作用的,可是设置最小堆内存大小是不起作用的,而且通过getMinimumHeapSize方法获取的大小为0

setTargetHeapUtilization(float newTarget) 可以设定内存利用率的百分比,当实际的利用率偏离这个百分比的时候,虚拟机会在GC的时候调整堆内存大小,让实际占用率向个百分比靠拢。

     //程序onCreate时调用 
    private final
    static floatTARGET_HEAP_UTILIZATION = 0.75f;

    VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION);

经过我多次测试,用mVMRuntime.getTargetHeapUtilization()方法查看,程序本来的利用率就0.75f,所以这个设置不设置都一样。

因此上面的方法都没有用,网上非常多像上面方法的转载都是浮云


安卓内存的溢出主要是使用图片时的内存溢出使用图片时怎样避免内存溢出呢?
问小华华的:
java.lang.Runtime这个类在安卓中还有用吗?它跟dalvik.system.VMRuntime类说的是同一个运行环境吗?
Runtime.getRuntime().totalMemory()freeMemory()/是否就是Davik虚拟机分配给app的大小和程序可用的内存大小?
答:android下每个应用程序都会开启一个对应的java虚拟机 可以这样理解吧,对于内存图片导致的内存溢出确实没有什么好的解决办法,只能试着从降低图片的分辨率上去解决了.

我的解决办法:
下面方法好像并没有损坏图片的质量。
通过下面的方法来设置
Activity的背景图片:

private Bitmap mBitmap;

       /** 设置背景图片 */
       private void setBackground() {
           mBitmap= BitmapUtil.readBitMap(this, R.drawable.top123_bg);
           rl_root.setBackgroundDrawable(new BitmapDrawable(mBitmap)); //rl_root为根布局对象
       }  

   //Activity中的大对象手动释放,主要有ListView、和ListView的适配器,和设置为背景图片的bitmap
   @Override
   rotected void onDestroy() {
       leftListTurnPageUtil = rightGridTurnPageUtil = null;
       listAdapter = null;
       gridAdapter = null;
       mGridView = null;
       mListView = null;
       if(!mBitmap.isRecycled()){
          mBitmap.recycle(); //回收图片所占的内存
       }
       System.gc(); //提醒系统及时回收
       super.onDestroy();
   }

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


解决方案参考自:
http://blog.csdn.net/dai_zhenliang  我在这里面转载了好几篇关于图片内存溢出的,有兴趣的可以看下,仅供参考,不一定能解决问题的,我的主要参考如下:
一、用
BitmapFactory.decodeStream方法来读取图片
尽量不要使用setImageBitmapsetImageResourceBitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageViewsourcedecodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间。如果在读取时加上图片的Config参数,可以跟有效减少加载的内存,从而更有效阻止抛out of Memory异常。另外,decodeStream直接拿的图片来读取字节码了, 不会根据机器的各种分辨率来自动适应, 使用了decodeStream之后,需要在hdpimdpildpi中配置相应的图片资源, 否则在不同分辨率机器上都是同样大小(像素点数量),显示出来的大小就不对了。
二、以最省内存的方式读取本地资源的图片

public static Bitmap readBitMap(Context context, int resId){

            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            options.inPurgeable = true;
            options.inInputShareable = true;
            InputStream is = context.getResources().openRawResource(resId); //获取资源图片的输入流
            return BitmapFactory.decodeStream(is,null, options);
       } 
三、以减少图片宽高的方式读取图片
1. public static Bitmap readBitMap2(Context context, int resId) {
       InputStream is = context.getResources().openRawResource(resId);
       BitmapFactory.Options options = new BitmapFactory.Options();
       options.inJustDecodeBounds = false;
       options.inSampleSize = 10; //widthhight设为原来的十分一
       return BitmapFactory.decodeStream(is,null,options);
  } 
2. if(!bmp.isRecycle() ){
        bmp.recycle() //
回收图片所占的内存
        system.gc()//
提醒系统及时回收
    }

希望对大家有帮助,更希望有其他的解决方法的朋友们贡献一下自己的解决经验。

http://sunhuichuan.blog.163.com/blog/static/1849445962013316115224803/
原创粉丝点击