Android Bitmap 内存以及OOM问题讨论

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Bitmap 内存以及OOM问题讨论相关的知识,希望对你有一定的参考价值。

参考技术A 都知道在android中, 每个应用所使用的内存是有限的,现在的手机通常最大的内存使用为256M, 目前还没发现Android中一个应用的最大内存分配超过256M的(经测试华为手机的最大内存是385M)

相关API:

ActivityManager.getMemoryClass(),首先获取系统服务中的ActivityManager

如下:

(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

可以获取到相关信息

最近一直被项目的OOM问题困扰, 在网上查阅相关资料,前前后后读了不下于30篇,这些篇幅讲解的东西都是千篇一律,并没有解决到实际问题

也在慕课网学习了内存优化章节

这是慕课网讲师的PPT,我截屏的

这里来仔细分析一下:

第一个,  注意临时Bitmap对象的及时回收, 来看下相关API

直接上图

经过我无数次的使用Android studio工具自带的MAT分析工具后, 得出一个很严谨的结论, 此方法并不奏效...

Android中Bitmap的内存存放在堆区, Google 的Bitmap的recycle方法注释也可以了解到

Android历史版本不是很清楚, 据说Android3.0之前Bitmap是存放在native区域,可以进行手动释放,然而3.0之后Bitmap是存放在Java层的堆区,没错是heap, 内存管理直接交由系统GC管理,你还这样释放资源有意义?无非是给自己的一点心理安慰罢了, 告诉你没卵用

又有人在说要释放内存使用System.gc() ???  对就是主动触发垃圾回收,这个API是开发者自行调用的吗?那么系统管理内存还有什么意义?这不是误人子弟吗,这个API不能调用的, 因为没卵用的, 具体自己参照MAT工具自行分析.即便垃圾回收真的被触发了, 所有线程停滞由系统来清理垃圾, 造成的后果是严重卡顿!!!

再看一个API:

我在网上苦苦追寻内存过高的问题后,发现了这个API,经过无数次实践后我得出一个结论,没卵用...开发者可以抛弃它

综上所诉, 第一点讲解的内存优化问题可以直接PASS掉, 无非给自己一点心理安慰: 我已经处理好了内存问题, 程序不会OOM?

第二点. 避免Bitmap的浪费

直接说结论, 这个是非常行之有效的,并且是一定能解决问题的

具体怎么操作呢? 自己实现LruCache这个类, 就是这么弄, 原理就是解码复用, 在内存中已经解码好的Bitmap直接拿出来使用, 没有的在加载到内存进行解析, 这个非常有效,但是并不能让你避免OOM

第三点, try-catch某些大内存分配的操作

这点上,我又要开始疑问了, 我Java功底不是很好

Java中发生内存溢出时,抛出的是OutOfMemoryError, 它的父类是VirtualMachineError

这玩意能catch住? 它属于Error范畴, 你能捕获?请Java大神出来说一下,我解释不清楚

第四点, 加载Bitmap 缩放比例, 解码格式, 局部加载

先来分析一下缩放比例:

按照市面上主流的手机分辨率来分析现在Android主流的分辨率是1920X1080, 如果一个ImageView控件刚好就是屏幕全屏,怎么说?直接占用掉8M内存, 想想一个实际的需求情况

一个查看大图的页面, 不断的关闭,打开查看新的大图,问题就来了,内存一直在暴增,迟早会突破界限OOM掉

分辨率是2K屏呢? 更恐怖了, 随着手机设备的屏幕分辨率提升, 加载图像需要的内存也是倍增的, 因为应用的最大内存并没有增加

结论: 缩放比例可以有效的降低内存占用问题, 但不是绝对的救命稻草, 该缩放的还是要缩放,而且必须缩放,就是采样  现在通常都是图片加载框架来完成,类似Glide.Picasso,Fresco等,他们可以帮助你减少工作量, 内存问题还是存在

再来分析一下解码格式:

这个跟缩放比例效果差不多, 只是同样的分辨率的图片加载到内存中时占用内存减少了

比如ARGB_8888 共32位

RGB_565  共16位

ARGB_4444 共16位

很明显这样格式图片加载的内存情况是ARGB_8888是其他格式的两倍内存

另外的问题是ARGB_8888看起来很清晰的, 其它的看起来图片有种糊了的感觉,自己选择吧

结论, 降低内存占用非常有效

最后一个是局部加载, 并没有怎么使用到,也不清楚就不说了

最后还有一个方法避免OOM, 开启largeHeap属性, 但是但是, 以前我们开启这个属性后被Oppo应用市场认定为占用内存过高, 不建议用户安装......所以我们又取消了!

总的来说, Bitmap在内存是变现的是不可控, 我项目OOM问题一直没有得到有效解决,因为图片编辑视频编辑之类的功能占用内存过高,继续使用OOM是必然的, 跟ios的同学交流了一番,他们说IOS的应用内存可以占用到1个G以上, 轻松跑到500M是没问题的, IOS的内存机制可以持续给内存使用, 具体我也不清楚,并且他们可以手动释放内存?malloc, free这样子?

如果大家有比较好的方案,还望留言交流互相帮助 [笑脸.gif]

补充: Android8.0开始Bitmap数据内存存在native层, 单个应用可用的内存显著增长, 极大的降低了OOM的概率(2018年3月22日)

bitmap 内存溢出OOM的解决办法分享

昨天遇到这个问题就是从一个输入流里调用BitmapFactory.decodeStream(this.getContentResolver().openInputStream(uri))得到一个bitmap报的错。第一次调用都没问题,第二次再次调用就会报上面那个内存溢出的问题。而且有的手机报有的手机不报。研究了半天终于解决。首先分析了下原因,应该是图片占用的内存超过了系统虚拟机可分配的最大限制。不同手机可能分配的最大值不一样。后来找到解决办法主要是设置BitmapFactory.Options。

    BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();  
      bitmapOptions.inSampleSize = 4;  
      bitmap = BitmapFactory.decodeStream(this.getContentResolver()..openInputStream(uri), null , bitmapOptions);  

有的说要加上bitmapOptions.inJustDecodeBounds = true;但是我加上后得到的bitmap就为null。去掉之后就可以。按照上面的方法就解决问题了。最好把bitmap在不用的时候回收一下:

if (bitmap != null && !bitmap.isRecycled())  
    bitmap.recycle();  

某位大神的图片占用内存的算法:
介绍一下图片占用进程的内存算法吧。
android中处理图片的基础类是Bitmap,顾名思义,就是位图。占用内存的算法如下:
图片的width*height*Config。
如果Config设置为ARGB_8888,那么上面的Config就是4。一张480*320的图片占用的内存就是480*320*4 byte。
前面有人说了一下8M的概念,其实是在默认情况下android进程的内存占用量为16M,因为Bitmap他除了java中持有数据外,底层C++的skia图形库还会持有一个SKBitmap对象,因此一般图片占用内存推荐大小应该不超过8M。这个可以调整,编译源代码时可以设置参数。

// Eliminate extra GCs during startup by setting the initial heap size to 4MB.  
// TODO: We should restore the old heap size once the activity reaches the idle state  
VMRuntime.getRuntime().setMinimumHeapSize(INITIAL_HEAP_SIZE);  

 

以上是关于Android Bitmap 内存以及OOM问题讨论的主要内容,如果未能解决你的问题,请参考以下文章

Android Bitmap深入介绍---基础

Android性能优化系列之Bitmap图片优化

APP性能优化系列:内存优化-bitmap详解

Android Bitmap深入介绍---基础

Android 内存优化,如何避免OOM

Android性能优化--OOM