java.lang.OutOfMemoryError:位图大小超出 VM 预算 - Android
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1949066/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
java.lang.OutOfMemoryError: bitmap size exceeds VM budget - Android
提问by Daniel Benedykt
I developed an application that uses lots of images on Android.
我开发了一个在 Android 上使用大量图像的应用程序。
The app runs once, fills the information on the screen (Layouts
, Listviews
, Textviews
, ImageViews
, etc) and user reads the information.
该应用程序运行一次,填满屏幕(上的信息Layouts
,Listviews
,Textviews
,ImageViews
,等)和用户读取的信息。
There is no animation, no special effects or anything that can fill the memory. Sometimes the drawables can change. Some are android resources and some are files saved in a folder in the SDCARD.
没有动画,没有特效或任何可以填满记忆的东西。有时可绘制对象会发生变化。有些是android资源,有些是保存在SDCARD文件夹中的文件。
Then the user quits (the onDestroy
method is executed and app stays in memory by the VM ) and then at some point the user enters again.
然后用户退出(该onDestroy
方法被执行并且应用程序由 VM 保留在内存中),然后在某个时候用户再次进入。
Each time the user enters to the app, I can see the memory growing more and more until user gets the java.lang.OutOfMemoryError
.
每次用户进入应用程序时,我都可以看到内存越来越大,直到用户获得java.lang.OutOfMemoryError
.
So what is the best/correct way to handle many images?
那么处理许多图像的最佳/正确方法是什么?
Should I put them in static methods so they are not loaded all the time? Do I have to clean the layout or the images used in the layout in a special way?
我应该将它们放在静态方法中以便它们不会一直加载吗?我是否必须以特殊方式清理布局或布局中使用的图像?
采纳答案by Trevor Johns
It sounds like you have a memory leak. The problem isn't handling many images, it's that your images aren't getting deallocated when your activity is destroyed.
听起来你有内存泄漏。问题不在于处理许多图像,而是当您的活动被销毁时,您的图像没有被释放。
It's difficult to say why this is without looking at your code. However, this article has some tips that might help:
如果不查看您的代码,很难说出为什么会这样。但是,本文有一些提示可能会有所帮助:
http://android-developers.blogspot.de/2009/01/avoiding-memory-leaks.html
http://android-developers.blogspot.de/2009/01/avoiding-memory-leaks.html
In particular, using static variables is likely to make things worse, not better. You might need to add code that removes callbacks when your application redraws -- but again, there's not enough information here to say for sure.
特别是,使用静态变量可能会使事情变得更糟,而不是更好。您可能需要添加在应用程序重绘时删除回调的代码——但同样,这里没有足够的信息来确定。
回答by hp.android
One of the most common errors that I found developing Android Apps is the “java.lang.OutOfMemoryError: Bitmap Size Exceeds VM Budget” error. I found this error frequently on activities using lots of bitmaps after changing orientation: the Activity is destroyed, created again and the layouts are “inflated” from the XML consuming the VM memory available for bitmaps.
我发现在开发 Android 应用程序时最常见的错误之一是“java.lang.OutOfMemoryError: Bitmap Size Exceeds VM Budget”错误。我经常在更改方向后使用大量位图的活动中发现此错误:活动被破坏,再次创建,并且布局从 XML 中“膨胀”,消耗了可用于位图的 VM 内存。
Bitmaps on the previous activity layout are not properly de-allocated by the garbage collector because they have crossed references to their activity. After many experiments I found a quite good solution for this problem.
垃圾收集器未正确取消先前活动布局上的位图的分配,因为它们已交叉引用其活动。经过多次实验,我找到了一个很好的解决这个问题的方法。
First, set the “id” attribute on the parent view of your XML layout:
首先,在 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"
>
...
Then, on the onDestroy()
method of your Activity, call the unbindDrawables()
method passing a reference to the parent View and then do a System.gc()
.
然后,在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();
}
}
This unbindDrawables()
method explores the view tree recursively and:
此unbindDrawables()
方法以递归方式探索视图树,并且:
- Removes callbacks on all the background drawables
- Removes children on every viewgroup
- 删除所有背景可绘制对象的回调
- 删除每个视图组上的子项
回答by ddmytrenko
To avoid this problem you can use native method Bitmap.recycle()
before null
-ing Bitmap
object (or setting another value). Example:
为避免此问题,您可以Bitmap.recycle()
在null
-ingBitmap
对象(或设置另一个值)之前使用本机方法。例子:
public final void setMyBitmap(Bitmap bitmap) {
if (this.myBitmap != null) {
this.myBitmap.recycle();
}
this.myBitmap = bitmap;
}
And next you can change myBitmap
w/o calling System.gc()
like:
接下来你可以改变myBitmap
w/o 调用,System.gc()
如:
setMyBitmap(null);
setMyBitmap(anotherBitmap);
回答by Donn Felker
I've ran into this exact problem. The heap is pretty small so these images can get out of control rather quickly in regards to memory. One way is to give the garbage collector a hint to collect memory on a bitmap by calling its recycle method.
我遇到了这个确切的问题。堆非常小,因此这些图像在内存方面可能会很快失控。一种方法是给垃圾收集器一个提示,通过调用它的回收方法来收集位图上的内存。
Also, the onDestroy method is not guaranteed to get called. You may want to move this logic/clean up into the onPause activity. Check out the Activity Lifecycle diagram/table on this pagefor more info.
此外,不能保证调用 onDestroy 方法。您可能希望将此逻辑/清理移动到 onPause 活动中。查看此页面上的活动生命周期图/表格以获取更多信息。
回答by Aron
This explanation might help: http://code.google.com/p/android/issues/detail?id=8488#c80
这个解释可能有帮助:http: //code.google.com/p/android/issues/detail?id=8488#c80
"Fast Tips:
“快速提示:
1) NEVER call System.gc() yourself. This has been propagated as a fix here, and it doesn't work. Do not do it. If you noticed in my explanation, before getting an OutOfMemoryError, the JVM already runs a garbage collection so there is no reason to do one again (its slowing your program down). Doing one at the end of your activity is just covering up the problem. It may causes the bitmap to be put on the finalizer queue faster, but there is no reason you couldn't have simply called recycle on each bitmap instead.
1) 永远不要自己调用 System.gc()。这已在此处作为修复程序进行传播,但它不起作用。不要做。如果您在我的解释中注意到,在出现 OutOfMemoryError 之前,JVM 已经运行了垃圾收集,因此没有理由再次执行此操作(它会减慢您的程序速度)。在你的活动结束时做一个只是掩盖了问题。它可能会导致位图更快地放入终结器队列,但没有理由不能简单地在每个位图上调用 recycle。
2) Always call recycle() on bitmaps you don't need anymore. At the very least, in the onDestroy of your activity go through and recycle all the bitmaps you were using. Also, if you want the bitmap instances to be collected from the dalvik heap faster, it doesn't hurt to clear any references to the bitmap.
2) 始终在不再需要的位图上调用 recycle()。至少,在您的活动的 onDestroy 中,遍历并回收您正在使用的所有位图。此外,如果您希望更快地从 dalvik 堆中收集位图实例,清除对位图的任何引用也无妨。
3) Calling recycle() and then System.gc() still might not remove the bitmap from the Dalvik heap. DO NOT BE CONCERNED about this. recycle() did its job and freed the native memory, it will just take some time to go through the steps I outlined earlier to actually remove the bitmap from the Dalvik heap. This is NOT a big deal because the large chunk of native memory is already free!
3) 调用 recycle() 和 System.gc() 仍然可能不会从 Dalvik 堆中删除位图。不要担心这个。recycle() 完成了它的工作并释放了本机内存,它只需要一些时间来完成我之前概述的步骤以从 Dalvik 堆中实际删除位图。这没什么大不了的,因为大块本机内存已经空闲了!
4) Always assume there is a bug in the framework last. Dalvik is doing exactly what its supposed to do. It may not be what you expect or what you want, but its how it works. "
4) 最后总是假设框架中存在错误。Dalvik 正在做它应该做的事情。它可能不是您期望的或您想要的,而是它的工作原理。”
回答by GSree
I had the exact same problem. After a few testing I found that this error is appearing for large image scaling. I reduced the image scaling and the problem disappeared.
我有同样的问题。经过几次测试后,我发现此错误出现在大图像缩放中。我减少了图像缩放,问题就消失了。
P.S. At first I tried to reduce the image size without scaling the image down. That did not stop the error.
PS 起初我试图在不缩小图像的情况下减小图像大小。这并没有阻止错误。
回答by GSree
Following points really helped me a lot. There might be other points too, but these are very crucial:
以下几点对我帮助很大。可能还有其他要点,但这些点非常关键:
- Use application context(instead of activity.this) where ever possible.
- Stop and release your threads in onPause() method of activity
- Release your views / callbacks in onDestroy() method of activity
- 尽可能使用应用程序上下文(而不是 activity.this)。
- 在 onPause() 活动方法中停止和释放线程
- 在 onDestroy() 活动方法中释放您的视图/回调
回答by Ranger Way
I suggest a convenient way to solve this problem. Just assign the attribute "android:configChanges" value as followed in the Mainfest.xml for your errored activity. like this:
我建议一个方便的方法来解决这个问题。只需在 Mainfest.xml 中为您的错误活动分配属性“android:configChanges”值。像这样:
<activity android:name=".main.MainActivity"
android:label="mainActivity"
android:configChanges="orientation|keyboardHidden|navigation">
</activity>
the first solution I gave out had really reduced the frequency of OOM error to a low level. But, it did not solve the problem totally. And then I will give out the 2nd solution:
我给出的第一个解决方案确实将 OOM 错误的频率降低到了一个较低的水平。但是,它并没有完全解决问题。然后我将给出第二个解决方案:
As the OOM detailed, I have used too much runtime memory. So, I reduce the picture size in ~/res/drawable of my project. Such as an overqualified picture which has a resolution of 128X128, could be resized to 64x64 which would also be suitable for my application. And after I did so with a pile of pictures, the OOM error doesn't occur again.
正如 OOM 所详述的那样,我使用了过多的运行时内存。因此,我减小了项目的 ~/res/drawable 中的图片大小。例如,分辨率为 128X128 的高品质图片可以调整为 64x64,这也适合我的应用程序。并且在我用一堆图片这样做之后,OOM 错误不再发生。
回答by subduedjoy
I too am frustrated by the outofmemory bug. And yes, I too found that this error pops up a lot when scaling images. At first I tried creating image sizes for all densities, but I found this substantially increased the size of my app. So I'm now just using one image for all densities and scaling my images.
我也对内存不足的错误感到沮丧。是的,我也发现在缩放图像时会经常出现此错误。起初我尝试为所有密度创建图像大小,但我发现这大大增加了我的应用程序的大小。所以我现在只对所有密度使用一张图像并缩放我的图像。
My application would throw an outofmemory error whenever the user went from one activity to another. Setting my drawables to null and calling System.gc() didn't work, neither did recycling my bitmapDrawables with getBitMap().recycle(). Android would continue to throw the outofmemory error with the first approach, and it would throw a canvas error message whenever it tried using a recycled bitmap with the second approach.
每当用户从一个活动转到另一个活动时,我的应用程序都会抛出内存不足错误。将我的 drawable 设置为 null 并调用 System.gc() 不起作用,使用 getBitMap().recycle() 回收我的 bitmapDrawables 也不起作用。Android 将继续使用第一种方法抛出内存不足错误,并且每当尝试使用第二种方法使用回收位图时,它都会抛出画布错误消息。
I took an even third approach. I set all views to null and the background to black. I do this cleanup in my onStop() method. This is the method that gets called as soon as the activity is no longer visible. If you do it in the onPause() method, users will see a black background. Not ideal. As for doing it in the onDestroy() method, there is no guarantee that it will get called.
我采取了第三种方法。我将所有视图设置为 null,将背景设置为黑色。我在 onStop() 方法中执行此清理。这是在活动不再可见时立即调用的方法。如果您在 onPause() 方法中执行此操作,用户将看到黑色背景。不理想。至于在 onDestroy() 方法中执行此操作,不能保证它会被调用。
To prevent a black screen from occurring if the user presses the back button on the device, I reload the activity in the onRestart() method by calling the startActivity(getIntent()) and then finish() methods.
为了防止在用户按下设备上的后退按钮时出现黑屏,我通过调用 startActivity(getIntent()) 和 finish() 方法在 onRestart() 方法中重新加载活动。
Note: it's not really necessary to change the background to black.
注意:实际上没有必要将背景更改为黑色。
回答by Li3ro
The BitmapFactory.decode* methods, discussed in the Load Large Bitmaps Efficiently lesson, should not be executed on the main UI thread if the source data is read from disk or a network location (or really any source other than memory). The time this data takes to load is unpredictable and depends on a variety of factors (speed of reading from disk or network, size of image, power of CPU, etc.). If one of these tasks blocks the UI thread, the system flags your application as non-responsive and the user has the option of closing it (see Designing for Responsiveness for more information).
如果源数据是从磁盘或网络位置(或实际上除内存之外的任何来源)读取的,则在有效加载大位图课程中讨论的 BitmapFactory.decode* 方法不应在主 UI 线程上执行。加载这些数据所需的时间是不可预测的,取决于多种因素(从磁盘或网络读取的速度、图像大小、CPU 的功率等)。如果这些任务之一阻塞了 UI 线程,系统会将您的应用程序标记为无响应,并且用户可以选择关闭它(有关更多信息,请参阅设计响应性)。