Android Memory Management, OutOfMemoryError

转载 2012年10月25日 10:45:03

A

   Android框架强制每个进程的24 MB内存限制。在一些旧的设备,如在G1,限制为16 MB 更低,更重要的是,由位图使用的内存限制。处理图像的应用程序,它是很容易达到此限制,并获得与OOM 异常死亡 的过程:E / dalvikvm堆(12517):1048576字节外部分配这个 过程中过大的E / GraphicsJNI(12517): VM将不会让我们分配1048576字节 / AndroidRuntime(12517):关闭VM / dalvikvm(12517):主题ID = 1:线程未捕获的异常退出(集团= 0x4001d7f0 ) E / AndroidRuntime(12517):致命异常:主要 电子/ AndroidRuntime(12517):java.lang.OutOfMemoryError:位图的大小超过VM的预算 ,这个限制是低得离谱 。设备,像512MB的物理RAM的Nexus之一,设置每个进程的前台活动只有5%的RAM的内存限制是一个愚蠢的错误 。但无论如何,事情是如何和我们生活-即找到如何解决它。

远远超过限制的内存分配方式有两种 :

  一种方法是从本机代码分配内存 。使用NDK(本地开发工具包)和JNI,它可能从C级(如的malloc / free或新建/删除)分配内存,这样的分配是不计入对24 MB的限制 。这是真的,从本机代码分配内存是为从Java方便,但它可以被用来存储在RAM中的数据(即使图像数据)的一些大金额 。

  另一种方式,其中的作品以及图像的,是使用OpenGL的纹理-纹理内存不计入限制 ,要查看您的应用程序确实分配多少内存可以使用android.os.Debug.getNativeHeapAllocatedSize( ),可以使用上面介绍的两种技术的Nexus之一,我可以轻松地为一个单一的前台进程分配300MB - 10倍以上的默认24 MB的限制 ,从上面来看使用navtive代码分配内存是不在24MB的限制内的(开放的GL的质地也是使用navtive代码分配内存的) 。

  每个 android 平台内存限制不一样,从最开始的 16M 到 24M,以及后来的 32M,64M,或许以后会更大。

  那如何获取单个 app 内存限制大小呢?

  class : ActivityManager

ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
activityManager.getMemoryClass();

  当然,ActivityManager 不单单限与此,许多对 android 程序管理的工具,都来源与此,或者从这里进行扩展。 

 


android不同设备单个进程可用内存是不一样的,可以查看/system/build.prop文件。

dalvik.vm.heapstartsize=5m
dalvik.vm.heapgrowthlimit=48m
dalvik.vm.heapsize=256m 

heapsize参数表示单个进程可用的最大内存,但如果存在如下参数:

dalvik.vm.heapgrowthlimit=48m表示单个进程内存被限定在48m,即程序运行过程中实际只能使用48m内存

android上的应用是java,当然需要虚拟机,而android上的应用是带有独立虚拟机的,也就是每开一个应用就会打开一个独立的虚拟机。这样设计的原因是可以避免虚拟机崩溃导致整个系统崩溃,但代价就是需要更多内存。以上这些设计确保了android的稳定性,正常情况下最多单个程序崩溃,但整个系统不会崩溃,也永远没有内存不足的提示出现。


 在Android中,一个Process 只能使用16M内存(?),要是超过了这个限定就会跳出这个异常

  For Android specific we should use the 'recycle' method rather than 'gc', because 'recycle' will free the memory at the same time, but calling 'gc' doesn't guaranty to run and free the memory for same time(if it is not too critical, we should not call gc in our code) and results can very every time.
One more thing using 'recycle' is faster than the 'gc' and it improves the performance.

即:bitmap.recycle();

  biamap=null;

效果要好于

  biamap=null;

  system.gc();

通过DDMS中的Heap选项卡监视内存情况:

1.Heap视图中部有一个Type叫做data object,即数据对象,也就是我们的程序中大量存在的类类型的对象。

2.在data object一行中有一列是“Total Size”,其值就是当前进程中所有Java数据对象的内存总量。

如果代码中存在没有释放对象引用的情况,则data object的Total Size值在每次GC后不会有明显的回落,随着操作次数的增多Total Size的值会越来越大,
  直到到达一个上限后导致进程被kill掉。

B  今天刚遇到的情况:发现gridview的getview中使用

复制代码
    @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            final View GridItem = mInflater.inflate(R.layout.store_catg_item,null, false);
       TextView text = (TextView) GridItem.findViewById(R.id.store_catg_item_text);
            ImageView cover = (ImageView) GridItem.findViewById(R.id.store_catg_item_cover);
            Bitmap coverimg = ImageUtilities.getCachedCover(magaList
                    .get(position).id+ReaderConfigures.THUMB_SUFFIX_PLANE);
            String title;
            if(isCatg){
                title= magaList.get(position).category;
                text.setText(title.toUpperCase());
            }else{
                title= magaList.get(position).pubname;
                text.setVisibility(View.INVISIBLE);
            }
            GridItem.setTag(title);
            cover.setImageBitmap(coverimg);
            return GridItem;
        }
复制代码

 

滑动时内存会不断涨,直到OutOfMemory,使用Holder后便不会发生该请况,具体原因未仔细查找,标记一下。

 1.对于常规开发者而言需要了解 Java的四种引用方式,比如强引用,软引用,弱引用以及虚引用。一些复杂些的程序在长期运行很可能出现类似OutOfMemoryError的异常。

2.并不要过多的指望gc,不用的对象可以显示的设置为空,比如obj=null,java的gc使用的是一个有向图,判断一个对象是否有效看的是其他的对象能到达这个对象的顶点,有向图的相对于链表、二叉树来说开销是可想而知。

3.Android为每个程序分配的对内存可以通过Runtime类的totalMemory() freeMemory() 两个方法获取VM的一些内存信息,

Runtime.getRuntime().freeMemory();

Formatter.formatFileSize(BaseActivity.baseContext,Runtime.getRuntime().freeMemory()));//格式化输出

对于系统heap内存获取,可以通过Dalvik.VMRuntime类的getMinimumHeapSize() 方法获取最小可用堆内存,同时显示释放软引用可以调用该类的gcSoftReferences() 方法,获取更多的运行内存。

4.对于多线程的处理,如果并发的线程很多,同时有频繁的创建和释放,可以通过concurrent类的线程池解决线程创建的效率瓶颈。

5. 不要在循环中创建过多的本地变量。

c

  The default heap size of android3.0 is 48M.Large background pictrue,button icon and the other pictrues used as ui all consume memory,and even if you have entered another activity,the resource of the previous activity still be keeped.So you had better not use the big pictrue in UI.

  在onDestroy中会用((BitmapDrawable)mBtn.getBackground()).setCallback(null)清理背景图。按道理来说图片资源应该已经清理掉了的。仔细看Bitmap的源代码,它其实起的作用是销毁java对象BitmapDrawable,而android为了提高效率,Bitmap真正的位图数据是在ndk中用c写的,所以用setCallback是不能销毁位图数据的,应该调用Bitmap的recycle()来清理内存。在onDestroy加上((BitmapDrawable)mBtn.getBackground()).getBitmap().recycle(),这样跑下来,内存情况很理想,不管在哪个activity中,使用的资源仅仅是当前activity用到的,就不会象之前到最后一个activity的时候,所有之前使用的资源都累积在内存中。

  但新的问题又出现了,当返回之前的activity时,会出现“try to use a recycled bitmap"的异常。这真是按了葫芦起了瓢啊,内心那个沮丧。。。没办法,继续分析。看来是后加上recycle引起的, 位图肯定在内存中有引用,在返回之前的activity时,因为位图数据其实已经被销毁了,所以才造成目前的情况。在看了setBackgroundResource的源码以后,恍然大悟,android对于直接通过资源id载入的资源其实是做了cache的了,这样下次再需要此资源的时候直接从cache中得到,这也是为效率考虑。但这样做也造成了用过的资源都会在内存中,这样的设计不是很适合使用了很多大图片资源的应用,这样累积下来应用的内存峰值是很高的。看了sdk后,我用:

Bitmap bm = BitmapFactory.decodeResource(this.getResources(), R.drawable.splash);
BitmapDrawable bd = new BitmapDrawable(this.getResources(), bm);

mBtn.setBackgroundDrawable(bd);

来代替mBtn.setBackgroundResource(R.drawable.splash)。

销毁的时候使用:

BitmapDrawable bd = (BitmapDrawable)mBtn.getBackground();

mBtn.setBackgroundResource(0);//别忘了把背景设为null,避免onDraw刷新背景时候出现used a recycled bitmap错误

bd.setCallback(null);
bd.getBitmap().recycle();

这样调整后,避免了在应用里缓存所有的资源,节省了宝贵的内存,而其实这样也不会造成太大效率问题,毕竟重新载入资源是非常快速,不会对性能造成很严重的影响,在xoom里我没有感受到和之前有什么区别。

总之,在android上使用大量位图是个比较痛苦的事,内存限制的存在对应用是个很大的瓶颈。但不用因噎费食,其实弄明白了它里面的机制,应用可以突破这些限制的。这只是其中的一种处理方法,还可以考虑BitmapFactory.Options的inSampleSize来减少内存占用。

浏览大图的应用,可以使用JNI的方法加载图片

相关文章推荐

Android签名机制之---签名验证过程详解

今天是元旦前夕,也是Single Dog的嚎叫之日,只能写博客来祛除寂寞了,今天我们继续来看一下Android中的签名机制的姊妹篇:Android中是如何验证一个Apk的签名。在前一篇文章中我们介绍了...

android签名机制

1.android为什么要签名

旋屏时,OnCreate方法重复调用的解决

Android设备屏幕自动横竖旋转时,每次都会重新调用onCreate函数进行初始化操作,如果遇到加载数据量稍微大点的时候就需要等待很长时间,用户体验较差,其实可以通过配置AndroidManifes...

Gradle build报错:Please correct the above warnings first解决方案

转载:gradle build报错:Please correct the above warnings first解决方案问题描述:在编译的时候,出现了下面的错误提示Warning: there we...

android系统信息(内存、cpu、sd卡、电量、版本)获取

要转载请注明出处:http://gqdy365.iteye.com/blog/1066113,有很多转载了文章不写出处,还写的是什么小编最近做项目碰到什么问题怎么解决的的然后把文章贴下面,俨然一副他们...

Android APK 签名机制

发布过Android应用的朋友们应该都知道,Android APK的发布是需要签名的。签名机制在Android应用和框架中有着十分重要的作用。例如,Android系统禁止更新安装签名不一致的APK;如...

android 弹出窗口

正常的Dialog弹出窗口,当点击窗口外围时关闭弹出的窗口,这个在很多弹出式的Activity上很为实用!   dialog.xml 弹出窗口的界面布局:   view p...

android apk 解包、打包、签名等一些操作的命名指导

首先下载apk解包、打包、签名所要的工具 http://download.csdn.net/detail/changcsw/6741573 下载后的压缩包中包含 解包、打包、签名 所用到的所以工具 ...

Android签名机制之---签名过程详解

一、前言又是过了好长时间,没写文章的双手都有点难受了。今天是圣诞节,还是得上班。因为前几天有一个之前的同事,在申请微信SDK的时候,遇到签名的问题,问了我一下,结果把我难倒了。。我说Android中的...

Android apk包的重签名

众所周知,apk包是必须要被签名的,否则无法在用户的终端上安装。为了满足某些需求,有时我们需要更换特定apk的签名证书和签名,在这篇文章中我们谈谈如何重新对一个apk包进行重签名。 我们需要分几步来实...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)