java.lang.OutOfMemoryError:位图大小超过VM预算 - Android

Dan*_*ykt 157 memory android memory-leaks bitmap out-of-memory

我开发了一个在Android上使用大量图像的应用程序.

该应用程序运行一次,填满屏幕上的信息(Layouts,Listviews,Textviews,ImageViews,等)和用户读取的信息.

没有动画,没有特效或任何可以填充内存的东西.有时候抽屉可以改变.有些是android资源,有些是保存在SDCARD文件夹中的文件.

然后用户退出(该onDestroy方法被执行并且app由VM保持在内存中)然后在某个时刻用户再次进入.

每次用户进入应用程序时,我都可以看到内存越来越多,直到用户获得java.lang.OutOfMemoryError.

那么处理许多图像的最佳/正确方法是什么?

我应该把它们放在静态方法中,这样它们就不会一直装载吗?我是否必须以特殊方式清洁布局或布局中使用的图像?

hp.*_*oid 96

我发现开发Android应用程序的最常见错误之一是"java.lang.OutOfMemoryError:Bitmap Size Exceeds VM Budget"错误.我在更改方向后使用大量位图的活动经常发现此错误:活动被销毁,再次创建,布局从消耗可用于位图的VM内存的XML中"膨胀".

前一个活动布局上的位图未被垃圾收集器正确解除分配,因为它们已经交叉引用了它们的活动.经过多次实验,我发现了一个很好的解决方案.

首先,在XML布局的父视图上设置"id"属性:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/RootView"
     >
     ...
Run Code Online (Sandbox Code Playgroud)

然后,在onDestroy() Activity 的方法上,调用unbindDrawables()方法将引用传递给父View,然后执行System.gc().

    @Override
    protected void onDestroy() {
    super.onDestroy();

    unbindDrawables(findViewById(R.id.RootView));
    System.gc();
    }

    private void unbindDrawables(View view) {
        if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
        }
        if (view instanceof ViewGroup) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
            }
        ((ViewGroup) view).removeAllViews();
        }
    }
Run Code Online (Sandbox Code Playgroud)

unbindDrawables()方法以递归方式探索视图树,并且:

  1. 删除所有背景drawable上的回调
  2. 删除每个视图组中的子项

  • 除了... java.lang.UnsupportedOperationException:AdapterView不支持removeAllViews() (10认同)
  • 或者只是更改条件:if(查看viewof ViewGroup &&!(view instanceof AdapterView)) (5认同)
  • @Adam Varhegyi,您可以在onDestroy中为库视图显式调用相同的函数.像unbindDrawables(galleryView); 希望这对你有用...... (2认同)

Tre*_*hns 70

听起来你有内存泄漏.问题不在于处理许多图像,而是当您的活动被破坏时,您的图像不会被解除分配.

很难说为什么没有看你的代码.但是,本文提供了一些可能有用的提示:

http://android-developers.blogspot.de/2009/01/avoiding-memory-leaks.html

特别是,使用静态变量可能会使事情变得更糟,而不是更好.您可能需要添加在应用程序重绘时删除回调的代码 - 但同样,这里没有足够的信息可以肯定地说.

  • 如果您怀疑内存泄漏,可以通过adb shell dumpsys meminfo命令快速监控堆使用情况.如果您看到堆使用量增加了几个gc周期(logcat中的GC_*日志行),您可以非常确定是否存在泄漏.然后通过adb或DDMS创建堆转储(或在不同时间的几个),并通过Eclipse MAT上的支配树工具进行分析.您很快就会发现哪个对象具有随时间增长的保留堆.那是你的内存泄漏.我在这里写了一篇文章,其中包含更多细节:http://macgyverdev.blogspot.com/2011/11/android-track-down-memory-leaks.html (15认同)
  • 这是真的,并且有很多图像(而不是内存泄漏)可能导致outOfMemory出于多种原因,例如许多其他应用程序正在运行,内存碎片等.我了解到,当你使用outOfMemory处理大量图像时系统地,就像总是一样,然后它是一个泄漏.如果你每天一次或类似的事情,它,因为你太接近极限.我在这种情况下的建议是尝试删除一些东西,然后再试一次.此外,图像内存位于堆内存之外,因此请注意两者. (8认同)

ddm*_*nko 10

要避免此问题,您可以Bitmap.recycle()null-ing Bitmap对象之前使用本机方法(或设置其他值).例:

public final void setMyBitmap(Bitmap bitmap) {
  if (this.myBitmap != null) {
    this.myBitmap.recycle();
  }
  this.myBitmap = bitmap;
}
Run Code Online (Sandbox Code Playgroud)

接下来你可以改变myBitmapw/o调用,System.gc()如:

setMyBitmap(null);    
setMyBitmap(anotherBitmap);
Run Code Online (Sandbox Code Playgroud)


Don*_*ker 7

我遇到了这个问题.堆非常小,因此这些图像可能会在内存方面相当快速地失控.一种方法是通过调用其循环方法为垃圾收集器提供一个提示来收集位图上的内存.

此外,不保证调用onDestroy方法.您可能希望将此逻辑/清理移动到onPause活动中.有关详细信息,请查看此页面上的活动生命周期图/表.


小智 7

此说明可能有所帮助:http: //code.google.com/p/android/issues/detail?id = 8488#c80

"快速提示:

1)永远不要自己调用System.gc().这已在此处作为修复传播,但它不起作用.不要做.如果您在我的解释中注意到,在获取OutOfMemoryError之前,JVM已经运行了垃圾收集,因此没有理由再次执行一次(它会减慢您的程序速度).在活动结束时执行一项操作就是解决问题.它可能会使位图更快地放在终结器队列中,但没有理由你不能简单地在每个位图上调用recycle.

2)总是在你不再需要的位图上调用recycle().至少,在您的活动的onDestroy中,通过并回收您正在使用的所有位图.此外,如果您希望更快地从dalvik堆中收集位图实例,则清除对位图的任何引用都没有什么坏处.

3)调用recycle()然后System.gc()仍然可能无法从Dalvik堆中删除位图.不要关心这个.recycle()完成了它的工作并释放了本机内存,它只需要一些时间来完成我前面概述的步骤,实际上从Dalvik堆中删除了位图.这不是什么大问题,因为大量本机内存已经免费!

4)始终假设框架中存在最后的错误.达尔维克正在做它应该做的事情.它可能不是您所期望的或您想要的,但它的工作原理."


GSr*_*ree 5

我有同样的问题.经过一些测试后,我发现这个错误出现在大图像缩放中.我减少了图像缩放,问题就消失了.

PS起初我尝试在不缩小图像的情况下缩小图像尺寸.这并没有阻止错误.

  • 你可以发布关于你如何进行图像缩放的代码,我面临同样的问题,我认为这可能会解决它.谢谢! (4认同)

小智 5

以下几点对我帮助很大.可能还有其他要点,但这些非常重要:

  1. 尽可能使用应用程序上下文(而不是activity.this).
  2. 在onPause()方法中停止并释放线程
  3. 在onDestroy()活动方法中释放您的视图/回调