文档章节

Android OOM

当空皓月
 当空皓月
发布于 2014/10/07 21:35
字数 1811
阅读 15120
收藏 33

Android在加载大背景图或者大量图片时,经常导致内存溢出(Out of Memory  Error),本文根据我处理这些问题的经历及其它开发者的经验,整理解决方案如下(部分代码及文字出处无法考证):
 方案一、读取图片时注意方法的调用,适当压缩  尽量不要使用setImageBitmapsetImageResourceBitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。 因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的  source,decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间。

         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);


如果在读取时加上图片的Config参数,可以跟有效减少加载的内存,从而跟有效阻止抛out of Memory异常。

 

   /**

     *  以最省内存的方式读取本地资源的图片

     *  @param context

     *  @param resId

     *  @return

      */

    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);

         }


另外,decodeStream直接拿图片来读取字节码,  不会根据机器的各种分辨率来自动适应,使用了decodeStream之后,需要在hdpi和mdpi,ldpi中配置相应的图片资源,  否则在不同分辨率机器上都是同样大小(像素点数量),显示出来的大小就不对了。
方案二、在适当的时候及时回收图片占用的内存  通常Activity或者Fragment在onStop/onDestroy时候就可以释放图片资源:  

 if(imageView !=  null &&  imageView.getDrawable() != null){     

      Bitmap oldBitmap =  ((BitmapDrawable) imageView.getDrawable()).getBitmap();    

       imageView.setImageDrawable(null);    

      if(oldBitmap !=  null){    

            oldBitmap.recycle();     

            oldBitmap =  null;   

      }    

 }   

 //  Other code.

 System.gc();


在释放资源时,需要注意释放的Bitmap或者相关的Drawable是否有被其它类引用。如果正常的调用,可以通过Bitmap.isRecycled()方法来判断是否有被标记回收;而如果是被UI线程的界面相关代码使用,就需要特别小心避免回收有可能被使用的资源,不然有可能抛出系统异常: E/AndroidRuntime: java.lang.IllegalArgumentException: Cannot draw recycled  bitmaps 并且该异常无法有效捕捉并处理。
方案三、不必要的时候避免图片的完整加载 只需要知道图片大小的情形下,可以不完整加载图片到内存。 在使用BitmapFactory压缩图片的时候,BitmapFactory.Options设置inJustDecodeBounds为true后,再使用decodeFile()等方法,可以在不分配空间状态下计算出图片的大小。示例:   

 BitmapFactory.Options opts =  new  BitmapFactory.Options();     

 //  设置inJustDecodeBounds为false     

 opts.inJustDecodeBounds = false;    

 //  使用decodeFile方法得到图片的宽和高    

 BitmapFactory.decodeFile(path,  opts);    

 //  打印出图片的宽和高

 Log.d("example", opts.outWidth + "," + opts.outHeight);

(ps:原理其实就是通过图片的头部信息读取图片的基本信息)
方案四、优化Dalvik虚拟机的堆内存分配  堆(HEAP)是VM中占用内存最多的部分,通常是动态分配的。堆的大小不是一成不变的,通常有一个分配机制来控制它的大小。比如初始的HEAP是4M大,当4M的空间被占用超过75%的时候,重新分配堆为8M大;当8M被占用超过75%,分配堆为16M大。倒过来,当16M的堆利用不足30%的时候,缩减它的大小为8M大。重新设置堆的大小,尤其是压缩,一般会涉及到内存的拷贝,所以变更堆的大小对效率有不良影响。 Heap  Utilization是堆的利用率。当实际的利用率偏离这个百分比的时候,虚拟机会在GC的时候调整堆内存大小,让实际占用率向个百分比靠拢。使用  dalvik.system.VMRuntime类提供的setTargetHeapUtilization方法可以增强程序堆内存的处理效率。  

 private final static float  TARGET_HEAP_UTILIZATION = 0.75f;    

 //  在程序onCreate时就可以调用

 VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION);


方案五、自定义堆(Heap)内存大小  对于一些Android项目,影响性能瓶颈的主要是Android自己内存管理机制问题,目前手机厂商对RAM都比较吝啬,对于软件的流畅性来说RAM对性能的影响十分敏感,除了优化Dalvik虚拟机的堆内存分配外,我们还可以强制定义自己软件的对内存大小,我们使用Dalvik提供的  dalvik.system.VMRuntime类来设置最小堆内存为例:  

 private final static int  CWJ_HEAP_SIZE = 6 * 1024 * 1024  ;

 VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE);  //  设置最小heap内存为6MB大小。


但是上面方法还是存在问题,函数setMinimumHeapSize其实只是改变了堆的下限值,它可以防止过于频繁的堆内存分配,当设置最小堆内存大小超过上限值(Max Heap  Size)时仍然采用堆的上限值,对于内存不足没什么作用。  
最后介绍一下图片占用进程的内存算法。android中处理图片的基础类是Bitmap,顾名思义,就是位图。占用内存的算法如:图片的width*height*Config。 如果Config设置为ARGB_8888,那么上面的Config就是4。一张480*320的图片占用的内存就是480*320*4  byte。 在默认情况下android进程的内存占用量为16M,因为Bitmap他除了java中持有数据外,底层C++的  skia图形库还会持有一个SKBitmap对象,因此一般图片占用内存推荐大小应该不超过8M。这个可以调整,编译源代码时可以设置参数。

参考资料:http://www.tuicool.com/articles/yemM7zf

方案六:在Manifest.xml文件里面的<application  里面添加Android:largeHeap="true"

简单粗暴。这种方法允许应用需要耗费手机很多的内存空间,但却是最快捷的解决办法

Android内存优化——常见内存泄露及优化方案:

https://www.jianshu.com/p/ab4a7e353076

如果一个无用对象(不需要再使用的对象)仍然被其他对象持有引用,造成该对象无法被系统回收,以致该对象在堆中所占用的内存单元无法被释放而造成内存空间浪费,这中情况就是内存泄露。

垃圾回收的机制主要是看对象是否有引用指向该对象。

java对象的引用包括:强(引用),弱(引用),软(引用),虚(引用)
https://www.cnblogs.com/huajiezh/p/5835618.html

我理解的对象的引用和垃圾回收:

1,GC回收对象时,一看有强引用指向了这个对象,你强,我不吃(不回收)。

2,GC回收对象时,一看有弱引用指向了这个对象,你弱,我吃(回收)。

3,GC回收对象时,一看有软引用指向了这个对象,看一下自己的内存,够用不吃,哎呀内存不够了,我吃。

4,虚引用在任何时候都可能被GC回收,。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

© 著作权归作者所有

共有 人打赏支持
当空皓月
粉丝 13
博文 179
码字总数 67070
作品 0
郑州
Android安全模型之Android安全机制(内存管理)

Ashmem匿名共享内存 Android的匿名共享内存(Ashmem)机制基于Linux内核的共享内存,但是Ashmem与cache shrinker关联起来,增加了内存回收算法的注册接口,因此Linux内存管理系统将不再使用内...

柳哥
2014/12/02
0
0
Android Bitmap变迁与原理解析(4.x-8.x)

App开发不可避免的要和图片打交道,由于其占用内存非常大,管理不当很容易导致内存不足,最后OOM,图片的背后其实是Bitmap,它是Android中最能吃内存的对象之一,也是很多OOM的元凶,不过,在...

看书的小蜗牛
05/22
0
0
Android 图片加载图片_OOM异常解决

http://stormzhang.github.io/android/2013/11/20/android-display-bitmaps-efficiently/ Android加载资源图片时,很容易出现OOM的错误。 因为Android系统对内存有一个限制,如果超出该限制,...

mstian06
2014/07/02
0
0
Android:内存控制及OOM处理

OOM(内存溢出)和Memory Leak(内存泄露)有什么关系? OOM可能是因为Memory Leak,也可能是你的应用本身就比较耗内存(比如图片浏览型的)。所以,出现OOM不一定是Memory Leak。 同样,Mem...

Xushao
2013/05/07
0
1
Android内存管理、监测剖析

Android内存管理机制 Android内存管理主要有:LowMemory Killer机制,Ashmem,PMEM/ION及Native内存和Dalvik内存管理管理和JVM垃圾回收机制。 LowMemory Killer机制 源码位置drivers/staging...

Jerikc
2015/01/14
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

windbg调试C源码级驱动

联机方式不多说了。我博客里有,英文的。 windbg联机文档 https://docs.microsoft.com/zh-cn/windows-hardware/drivers/debugger/debug-universal-drivers---step-by-step-lab--echo-kernel......

simpower
35分钟前
0
0
redis快照和AOF简介

数据持久化到硬盘:一是快照(snapshotting),二是只追加文件(append-only file AOF) 快照 核心原理:redis某个时间内存内的所有数据写入硬盘 场景:redis快照内存里面的数据 1. 用户发送bgsav...

拐美人
35分钟前
0
0
这个七夕,送你一份程序员教科书级别的告白指南

给广大爱码士们的高能预警: 今天,就是七夕了…… (单身非作战人群请速速退场!) 时常有技术GG向个推君抱怨 经过网民多年的教育 以及技术人持之以恒的自黑 冲锋衣狂热分子·格子衫骨灰级粉...

个推
40分钟前
0
0
python爬虫日志(15)cookie详解

转载:原文地址 早期Web开发面临的最大问题之一是如何管理状态。服务器端没有办法知道两个请求是否来自于同一个浏览器。那时的办法是在请求的页面中插入一个token,并且在下一次请求中将这个...

茫羽行
41分钟前
0
0
qlv视频格式转换器

  腾讯视频中的视频影视资源有很多,小编经常在里面下载视频观看,应该也有很多朋友和小编一样吧,最近热播的电视剧也不少,如《香蜜沉沉烬如霜》、《夜天子》还有已经完结的《扶摇》,这么...

萤火的萤火
44分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部