1.什么是OOM?
03-21 21:05:28 . 771: e/dalvikvm-heap(13316): out of memory on a 10485776-byte allocation .
03-21 21:05:28.7793360 e/Android运行时(13316): Java . lang . out of memory error
这几句话的意思是,我们的程序申请需要10485776byte太大,虚拟机无法满足我们,可耻的shutdown自杀了。这种现象通常出现在使用大量图片或大图片的APP开发中。一般来说,当APP需要申请一块内存来安装照片时,系统认为APP已经使用了足够的内存。(大卫亚设,Northern Exposure(美国电视新闻),即使有1G的空闲内存,我也不同意为APP提供更多内存。然后,系统会立即产生OOM错误,如果程序没有捕获错误,炸弹箱就会崩溃。
2.为什么有OOM?
如果请求的内存资源超过此限制,则会出现OOM错误,因为Android系统中的每个app的每个进程或每个虚拟机都有最大内存限制。与整个设备的其馀内存没有太大关系。例如,以前Android系统的虚拟机最多有16M的内存,当app启动后,虚拟机将继续请求内存资源装载图片,如果超过内存限制,将出现OOM。Android系统的APP内存限制是如何确定的?
2.1 Android的APP内存组件:
APP内存由Java堆dalvik内存和本地内存两部分组成。在此处创建的对象将分配给此处,native是通过c/c请求的内存。位图是以这种方式分配的。Android3.0之后的系统默认通过dalvik分配,native作为堆进行管理。这两部分加起来不能超过Android对单个进程、虚拟机的内存限制。
每个手机的内存限制大小是多少?
activity manager activity manager=(activity manager)
AC();
上述方法返回以M为单位的数字,每个系统平台或设备的值都不同,如HTC基本24M、galaxy 36M、EMULA24M等。我的座右铭xt681是42米。3
以上是虚拟机的最大内存资源。
对于Head堆大小限制,可以查看/system文件。
Dalvik.vm.heapstartsize=5m
Dalvik.vm.heapgrowthlimit=48m
Dalvik.vm.heapsize=256m
注:heapsize参数表示单个进程heap可以使用的最大内存,但如果存在以下参数“dalvik.vm.headgrowthlimit=48m”,则表示单个进程heap内存限制为48m。换句话说,程序执行过程实际上只能使用48m内存。
2.2 Android系统为什么设置APP的内存限制?
1开发人员内存使用应更加合理。通过限制每个应用程序可用的最大内存限制,某些应用程序可能会恶意或无意地使用太多内存。这导致其他应用程序无法正常工作。Android是一个多进程,因此,如果一个进程(即应用程序)使用了太多内存,则无法运行其他应用程序。因为有局限性,开发人员必须善用有限的资源,优化资源使用。
2屏幕显示内容有限,内存足够就可以了。需要使用数千万张照片上千万个数据,但在特定时间点需要向用户显示的东西总是有限的。因为屏幕显示太大,可以放在上面的信息有限。大部分信息都已准备好显示,因此不需要给heap内存太多。也就是说,出现OOM现象,大多数原因是我们的程序设计有问题,需要优化。优化方法有很多。例如,通过时间改变空间,继续加载要使用的照片,继续回收不使用的照片,将大照片分析为适合手机屏幕大小的图片。
3需要多个APP多个虚拟机davlik限制。Android的app使用独立虚拟机,每次打开应用程序时都会打开一个或多个独立虚拟机。这样可以防止虚拟机崩溃导致整个系统崩溃,需要浪费更多内存。这个设计保证了Android的稳定性。
不是2.3 GC自动回收资源吗?为什么会有OOM?
Android不会使用GC自动回收资源。为什么app的未使用资源不被回收利用?
Android的GC根据特定算法回收程序未使用的内存资源,防止app的内存应用程序大致堆积,但GC通常回收的资源是无主的对象内存、软参考资源或软参考资源。例如:
Bitmap Bt=bi (this.getresources()、r . drawable . splash);//此时照片资源是强参考,是主人所在的资源。
Bt=null//此时,这幅画资源是无主的。Gc心情号码去回收。
softreferencebitmap soft ref=new softreferencebitmap(Bt);
Bt=null
其他代码……。
当程序请求大量内存资源时,GC可以释放softref引用的此图片内存。Bt=(),此时可以
能得到的是null,需要重新加载图片。当然这也说明了用软引用图片资源的好处,就是gc会自动根据需要释放资源,一定程度上避免OOM。
TIPS:编程要养成习惯,不用的对象设置为null。其实更好的是,不用的图片直接recycle。因为通过设置null让gc来回收,有时候还是会来不及。
2.4 怎么查看APP内存分配情况?
1 通过DDMS中的heap选项卡监视内存情况:
Heap视图中部有一个叫做data object, 即数据对象,也就是我们的程序中大量存在的类类型的对象。
在data object一行中有一列是"Total Size", 其值就是当前进程中所有Java数据对象的内存总量。如果代码中存在没有释放对象引用的情况,则data object的"Total Size"值在每次gc后不会有美线的回落。随着操作次数的增加"Total Size"的值会越来越大。直到到达一个上限 后导致进程被kill掉。
2 在App里面我们可以通过totalMemory与freeMemory:
Run().freeMemory()
RUn().totalMemory()
3 adb shell dumpsys meminfo com.android.demo
3. 常见避免OOM的几个注意点:
3.1 适当调整图像大小 。因为手机屏幕尺寸有限,分配给图像的显示区域有限,尤其对于超大图片,加载自网络或者sd卡,图片文件提及达到几M或者十几个M的:
加载到内存前,先算出该bitmap的大小,然后通过适当调节采样率使得加载的图片刚好,或稍大捷克在手机屏幕上显示就满意了:
Bim opts = new Bi();
o = true ;
o(opts, minSideLength, maxNumOfPixels); // Android 提供了一种动态计算的方法 computeSampleSize
o = false ;
try {
return Bi(imageFile, opts);
} catch (OutOfMemoryError err){
}
3.2 图像缓存 。在listview或Gallery等控件中一次性加载大量图片时,只加载屏幕显示的资源,尚未显示的不加载,移出屏幕的资源及时释放,采用强引用+软引用2级缓存,提高加载性能。缓存图像到内存,采用软引用缓存到内存,而不是在每次使用的时候都从新加载到内存。
3.3 采用低内存占用量的编码方式 。比如Bi比Bi更省内存。
3.4 及时回收图像 。如果引用了大量的Bitmap对象,而应用又不需要同时显示所有图片。可以将暂时不用到的Bitmap对象及时回收掉。对于一些明确直到图片使用情况的场景可以主动recycle回收
App的启动splash画面上的图片资源,使用完就recycle。对于帧动画,可以加载一张,画一张,释放一张。
3.5 不要在循环中创建过多的本地变量 。慎用static,用static来修饰成员变量时,该变量就属于该类,而不是该类实例,它的生命周期是很长的。如果用它来引用一些内存占用太多的实例,这时候就要谨慎对待了。
3.6 自定义堆内存分配大小 。优化Dalvik虚拟机的堆内存分配。
public class ClassName{
private static Context mContext;
// 省略
}
4. App使用图片时避免OOM的几种方式:
4.1 直接null或recycle
对于app里使用的大量图片,采用方式:使用时加载,不显示时直接置null或recycle。
这样处理是个好习惯,记本可以杜绝OOM,但是缺憾是代码多了,可能会忘记某些资源recycle。
而有些情况下会出现特定图片反复加载,释放,再加载等,低效率的事情。
4.2 简单通过SoftReference引用方式管理图片资源
建个SoftReference的hashmap
使用图片时先查询这个hashmap是否有softreference, softreference里的图片是否为空,
如果为空就加载图片到softreference并加入hashmap。
无需再代码里显式的处理图片的回收与释放,gc会自动处理资源的释放。
这种方式处理起来简单实用,能一定程度上避免前一种方法反复加载释放的低效率。但还不够优化。
4.3 强引用+软引用二级缓存
Android示范程序ImageDownloader.java, 使用了一个二级缓存机制。就是有一个数据结构直接持有解码成功的Bitmap对象引用,同时使用一个二级缓存数据结构保持淘汰的Bitmap的softreference对象,由于softreference对象的特殊性,系统会再需要内存的时候首先将softreference持有的对象释放掉,也就是说当vm发现可用的内存较少需要出发gc的时候,二级缓存中的bitmap对象将被回收,而持有一级缓存的bitmap对象用于显示。
其实这个解决方案最为关键的一点是使用了一个比较合适的数据结构,那就是LinkedHashMap类型来进行一级缓存Bitmap的容器。由于LinkeHashMap的特殊性,我们可以控制其内存存储对象的个数并且将不在使用的对象从容器中移除,放到softreference二级缓存里,我们可以在一级缓存中一致保存最近被访问到的bitmap对象,而已经被访问过的图片在LinkedHashMap的容量超过我们预设值时将会把容器中存在的时间最长的对象移除,这个时候我么可以将被移除的LinkedHashMap中的放到二级缓存容器,而二级缓存中的对象管理就交给系统来做了,当系统需要gc时就会首先回收二级缓存容器的Bitmap对象了。
在获取图片对象时候先从一级缓存容器中查找,如果有对应对象并可用直接返回,如果没有的话从二级缓存中查找对应的SoftReference, 判断SoftReference对象持有的Bitmap是否可用,可用直接返回,否则返回空。如果二级缓存都找不到图片,那就直接加载图片资源。
4, LruCache + sd的缓存方式
5. 两种容易OOM的场景建议:
5.1 网络下载大量图片
比如微博客户端: 多线程异步网络,小兔直接用LRUCache+SoftRef+Sd,大图按需下载:
5.2 对于需要加载非常多条目信息的listview,gridview等的情况
在adapter的getView函数里有个convertView参数,告知你是否有可重用的view对象。 如果不使用convertView的话,每次调用getView时每次都会重新创建view,这样之前的view可能还没销毁,加之不断的新建view势必会造成内存剧增,从而导致OOM。另外在重用convertView时,里面原有的图片等资源就会变成无主的了。
这里Google官方推荐使用:"convertview+静态类viewholder"
官方给出解释是:
a 重用缓存convertView传递给getView()方法来避免填充不必要的视图。
b 使用ViewHolder模式来避免没有必要的调用findViewById;因为太多的findViewById也会影响性能。
附ViewHolder类的作用:ViewHolder模式通过在getView方法返回的视图的标签(tag)中存储一个数据结构。这个数据结构包含了指向我们要绑定数据的视图的引用,从而避免每次调用getView()的时候调用findViewById();
6 申请超过内存限制的内存分配方式:
6.1 从Native C分配内存。使用NDK(本地开发工具包)和JNI, 它可能从C级(如malloc/free或新建/删除)分配内存,这样的分配是不计入24MB的限制。这是真的,从本机代码分配内存是为了java方便,但它可以被用来存储在ram的数据(即使图片数据)的一些打击呢。
6.2 使用OpenGL的纹理。纹理内存不计入限制,要查看你的应用程序确实分配了多少内存可以使用android.os.Debug.getNativeHeapAllocatedSize(), 可以使用上面介绍的两种技术的Nexus之一,我可以轻松地为一个单一的前台进程分配300MB-10倍以上的默认24MB 的限制,从上面看来使用native代码分配内存是不在24MB的限制内的(开放的GL的质地也是使用native代码分配内存)。
但是,这两个方法的风险就是,本地堆分配内存超过系统可用内存限制的话,通常都是直接崩溃。
1.《【cfoutofmemory怎么解决】Android应用OOM问题分析及解决方案》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《【cfoutofmemory怎么解决】Android应用OOM问题分析及解决方案》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/gl/2561848.html