03-21 21:05:28.771: E/dalvikvm-heap(13316): Out of memory on a 10485776-byte allocation. 03-21 21:05:28.779: E/AndroidRuntime(13316): java.lang.OutOfMemoryError
这几句的意思是,咱们程序申请须要10485776byte太大了,虚拟机没法知足咱们,羞愧的shutdown自杀了。这个现象一般出如今用到不少图片或者很大图片的APP开发中。通俗讲就是当咱们的APP须要申请一块内存来装图片的时候,系统以为咱们的APP所使用的内存已经够多了。即便它有1G的空余内存,它不一样意给个人APP更多的内存里,而后即便系统立刻抛出OOM错误,而程序没有捕捉该错误,故弹框崩溃了。
java
由于android系统的app的每一个进程或者每一个虚拟机有个最大内存限制,若是申请的内存资源超过这个限制,系统就会抛出OOM错误。跟整个设备的剩余内存没太大关系。好比比较早的android系统的一个虚拟机最多16M内存,当一个app启动后,虚拟机不停的申请内存资源来装载图片,当超过内存上限时就出现OOM。Android系统的APP内存限制怎么肯定?
android
APP内存由 dalvik内存 和 native内存 2部分组成,dalvik也就是java堆,建立的对象就是就是在这里分配的,而native是经过c/c++方式申请的内存,Bitmap就是以这种方式分配的。(android3.0之后,系统都默认经过dalvik分配的,native做为堆来管理)。这2部分加起来不能超过android对单个进程,虚拟机的内存限制。 每一个手机的内存限制大小是多少?c++
ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE) activityManager.getMemoryClass();
以上方法会返回以M为单位的数字,不一样的系统平台或设备上的值都不太同样,好比HTC默认24M, Galaxy36M, emulator-2.3 24M,等等。个人moto xt681是42M。3 上面取到是虚拟机的最大内存资源。 而对于head堆的大小限制,能够查看 /system/build.prop 文件。算法
dalvik.vm.heapstartsize = 5m dalvik.vm.heapgrowthlimit=48m dalvik.vm.heapsize=256m
注: heapsize参数表示单个进程heap可用的最大内存,但若是存在如下参数"dalvik.vm.headgrowthlimit =48m"表示单个进程heap内存被限定在48m,即程序运行过程实际只能使用48m内存。
shell
1 要使开发者内存使用更为合理。 限制每一个应用的可用内存上限,能够放置某些应用程序恶意或者无心的使用过多的内存。而致使其它应用没法正常运行。Android是有多进程的,若是一个进程(就是一个应用)耗费过多的内存,其余应用就没法运行了。由于有了限制,使得开发者必须好好利用有限资源,优化资源的使用。
2 屏幕显示内容有限,内存足够便可。 即便有万千图片千万数据须要使用到,但在特定时刻须要展现给用户看的老是有限的,由于屏幕显示就那么大,上面能够放的信息就是颇有限的。大部分信息都是处于准备显示状态,因此不必给予太多heap内存。也就是说出现 OOM现象,绝大部分缘由是咱们的程序设计上有问题,须要优化 。优化方法不少,好比经过时间换空间,不停的加载要用的的图片,不停的回收不用的图片,把大图片解析成适合手机屏幕大小的图片等。
3 多APP多个虚拟机davlik的限制须要。 android上的app使用独立虚拟机,每开一个应用就会打开至少一个独立的虚拟机。这样能够避免虚拟机崩溃致使整个系统崩溃,同时代价就是须要浪费更多的内存。这样设计保证了android的稳定性。
编程
Android不是用GC会自动回收资源么,为何app的那些不用的资源不回收呢? Android的gc会按照特定的算法回收程序不用的内存资源,避免app的内存申请约积越多,可是gc通常回收的资源是那些无主的对象内存或者软饮用的资源,或者更软引用的引用资源。好比: 缓存
Bitmap bt = BitmapFactory.decodeResource(this.getResources(), R.drawable.splash); //此时的图片资源是强引用,是有主的资源。 bt = null; //此时这个图片资源就是无主的了。gc心情号的时候就会去回收它。 SoftReference<Bitmap> softRef = new SoftReference<Bitmap>(bt); bt = null; 其余代码...
当程序申请不少内存资源时,gc有可能会释放softref引用的这个图片内存。bt=softRef.get(),此时可能获得的是null,须要从新加载图片。 固然这也说明了用软引用图片资源的好处,就是gc会自动根据须要释放资源,必定程度上避免OOM。
TIPS:编程要养成习惯,不用的对象设置为null。其实更好的是,不用的图片直接recycle。由于经过设置null让gc来回收,有时候仍是会来不及。
网络
1 经过DDMS中的heap选项卡监视内存状况 : Heap视图中部有一个叫作data object, 即数据对象,也就是咱们的程序中大量存在的类类型的对象。 在data object一行中有一列是“Total Size”, 其值就是当前进程中全部Java数据对象的内存总量。若是代码中存在没有释放对象引用的状况,则data object的“Total Size”值在每次gc后不会有美线的回落。随着操做次数的增长“Total Size”的值会愈来愈大。直到到达一个上限 后致使进程被kill掉。 2 在App里面咱们能够经过totalMemory与freeMemory:数据结构
Runtime.getRuntime().freeMemory() RUntime.getRuntime().totalMemory()
3 adb shell dumpsys meminfo com.android.demo
多线程
3.1 适当调整图像大小 。由于手机屏幕尺寸有限,分配给图像的显示区域有限,尤为对于超大图片,加载自网络或者sd卡,图片文件说起达到几M或者十几个M的: 加载到内存前,先算出该bitmap的大小,而后经过适当调节采样率使得加载的图片恰好,或稍大捷克在手机屏幕上显示就满意了:
BimtapFactory.Option opts = new BitampFactory.Option(); opts.inJustDecodeBounds = true; opts.inSampleSize=computeSample(opts, minSideLength, maxNumOfPixels); // Android 提供了一种动态计算的方法 computeSampleSize opts.inJustDecodeBounds = false; try{ return BitmapFactory.decodeFile(imageFile, opts); } catch(OutOfMemoryError err){ }
3.2 图像缓存 。在listview或Gallery等控件中一次性加载大量图片时,只加载屏幕显示的资源,还没有显示的不加载,移出屏幕的资源及时释放,采用强引用+软引用2级缓存,提升加载性能。缓存图像到内存,采用软引用缓存到内存,而不是在每次使用的时候都重新加载到内存。 3.3 采用低内存占用量的编码方式 。好比Bitmap.Config.ARGB_4444比Bitmap.Config.ARGB_8888更省内存。 3.4 及时回收图像 。若是引用了大量的Bitmap对象,而应用又不须要同时显示全部图片。能够将暂时不用到的Bitmap对象及时回收掉。对于一些明确直到图片使用状况的场景能够主动recycle回收 App的启动splash画面上的图片资源,使用完就recycle。对于帧动画,能够加载一张,画一张,释放一张。 3.5 不要在循环中建立过多的本地变量 。慎用static,用static来修饰成员变量时,该变量就属于该类,而不是该类实例,它的生命周期是很长的。若是用它来引用一些内存占用太多的实例,这时候就要谨慎对待了。 3.6 自定义堆内存分配大小 。优化Dalvik虚拟机的堆内存分配。
public class ClassName{ private static Context mContext; // 省略 }
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.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.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代码分配内存)。 可是,这两个方法的风险就是,本地堆分配内存超过系统可用内存限制的话,一般都是直接崩溃。