如何解决Android中的java.lang.OutOfMemoryError故障
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25719620/
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
How to solve java.lang.OutOfMemoryError trouble in Android
提问by Utku Soyta?
Altough I have very small size image in drawable folder, I am getting this error from users. And I am not using any bitmap function in code. At least intentionally :)
尽管我在 drawable 文件夹中有非常小的图像,但我从用户那里收到了这个错误。而且我没有在代码中使用任何位图函数。至少是故意的:)
java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:683)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:513)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:889)
at android.content.res.Resources.loadDrawable(Resources.java:3436)
at android.content.res.Resources.getDrawable(Resources.java:1909)
at android.view.View.setBackgroundResource(View.java:16251)
at com.autkusoytas.bilbakalim.SoruEkrani.cevapSecimi(SoruEkrani.java:666)
at com.autkusoytas.bilbakalim.SoruEkrani.run(SoruEkrani.java:862)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:146)
at android.app.ActivityThread.main(ActivityThread.java:5602)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
at dalvik.system.NativeStart.main(Native Method)
According to this stackTrace I'm gettin this error at this line ('tv' is a textView):
根据这个stackTrace,我在这一行出现了这个错误('tv'是一个textView):
tv.setBackgroundResource(R.drawable.yanlis);
What is the problem? If you need some other information about code, I can add it. Thanks!
问题是什么?如果您需要有关代码的其他信息,我可以添加它。谢谢!
回答by Maveňツ
You can't increase the heap size dynamically but you can request to use more by using.
您不能动态增加堆大小,但可以通过 using 请求使用更多。
android:largeHeap="true"
机器人:大堆=“真”
in the manifest.xml
,you can add in your manifest these lines it is working for some situations.
在 中manifest.xml
,您可以在清单中添加这些行,它适用于某些情况。
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:supportsRtl="true"
android:theme="@style/AppTheme">
Whether your application's processes should be created with a large Dalvik heap. This applies to all processes created for the application. It only applies to the first application loaded into a process; if you're using a shared user ID to allow multiple applications to use a process, they all must use this option consistently or they will have unpredictable results. Most apps should not need this and should instead focus on reducing their overall memory usage for improved performance. Enabling this also does not guarantee a fixed increase in available memory, because some devices are constrained by their total available memory.
是否应使用大型 Dalvik 堆创建应用程序的进程。这适用于为应用程序创建的所有进程。它仅适用于加载到进程中的第一个应用程序;如果您使用共享用户 ID 来允许多个应用程序使用一个进程,则它们都必须一致地使用此选项,否则它们将产生不可预测的结果。大多数应用程序不应该需要这个,而是应该专注于减少整体内存使用以提高性能。启用此功能也不能保证可用内存的固定增加,因为某些设备受到其总可用内存的限制。
To query the available memory size at runtime, use the methods getMemoryClass()
or getLargeMemoryClass()
.
要在运行时查询可用内存大小,请使用方法getMemoryClass()
或getLargeMemoryClass()
。
If still facing problem then this should also work
如果仍然面临问题,那么这也应该有效
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
mBitmapInsurance = BitmapFactory.decodeFile(mCurrentPhotoPath,options);
如果设置为 > 1 的值,则请求解码器对原始图像进行二次采样,返回较小的图像以节省内存。
This is the optimal use of BitmapFactory.Options.inSampleSize with regards to speed of displaying the image. The documentation mentions using values that are a power of 2, so I am working with 2, 4, 8, 16 etc.
这是 BitmapFactory.Options.inSampleSize 在显示图像速度方面的最佳使用。文档提到使用 2 的幂的值,所以我正在使用 2、4、8、16 等。
Lets get more deeper to Image Sampling:
让我们更深入地了解图像采样:
For example, it's not worth loading a 1024x768 pixel image into memory if it will eventually be displayed in a 128x128 pixel thumbnail in an ImageView
.
例如,如果一个 1024x768 像素的图像最终会显示在ImageView
.
To tell the decoder to subsample the image, loading a smaller version into memory, set inSampleSize
to true
in your BitmapFactory.Options
object. For example, an image with resolution 2100 x 1500 pixels that is decoded with an inSampleSize
of 4 produces a bitmap of approximately 512x384. Loading this into memory uses 0.75MB rather than 12MB for the full image (assuming a bitmap configuration of ARGB_8888
). Here's a method to calculate a sample size value that is a power of two based on a target width and height:
告诉解码器对图像进行二次采样,将较小的版本加载到内存中,在您的对象中设置inSampleSize
为。例如,分辨率为 2100 x 1500 像素的图像使用4 进行解码会生成大约 512x384 的位图。将其加载到内存中使用 0.75MB 而不是完整图像的 12MB(假设位图配置为)。这是一种基于目标宽度和高度计算样本大小值的方法,该值是 2 的幂:true
BitmapFactory.Options
inSampleSize
ARGB_8888
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
Note: A power of two value is calculated because the decoder uses a final value by rounding down to the nearest power of two, as per the
inSampleSize
documentation.
注意:计算二值的幂是因为解码器通过四舍五入到最接近的二的幂来使用最终值,根据
inSampleSize
文档。
To use this method, first decode with inJustDecodeBounds
set to true
, pass the options through and then decode again using the new inSampleSize
value and inJustDecodeBounds
set to false
:
要使用此方法,首先使用inJustDecodeBounds
set to 进行解码true
,传递选项,然后使用新inSampleSize
值再次解码并inJustDecodeBounds
设置为false
:
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
This method makes it easy to load a bitmap of arbitrarily large size into an ImageView
that displays a 100x100 pixel thumbnail, as shown in the following example code:
这种方法可以很容易地将任意大尺寸的位图加载到ImageView
显示 100x100 像素缩略图的图像中,如以下示例代码所示:
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
You can follow a similar process to decode bitmaps from other sources, by substituting the appropriate BitmapFactory.decode*
method as needed.
通过BitmapFactory.decode*
根据需要替换适当的方法,您可以按照类似的过程对来自其他来源的位图进行解码。
I found this code also interesting:
我发现这段代码也很有趣:
private Bitmap getBitmap(String path) {
Uri uri = getImageUri(path);
InputStream in = null;
try {
final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
in = mContentResolver.openInputStream(uri);
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, o);
in.close();
int scale = 1;
while ((o.outWidth * o.outHeight) * (1 / Math.pow(scale, 2)) >
IMAGE_MAX_SIZE) {
scale++;
}
Log.d(TAG, "scale = " + scale + ", orig-width: " + o.outWidth + ",
orig-height: " + o.outHeight);
Bitmap bitmap = null;
in = mContentResolver.openInputStream(uri);
if (scale > 1) {
scale--;
// scale to max possible inSampleSize that still yields an image
// larger than target
o = new BitmapFactory.Options();
o.inSampleSize = scale;
bitmap = BitmapFactory.decodeStream(in, null, o);
// resize to desired dimensions
int height = bitmap.getHeight();
int width = bitmap.getWidth();
Log.d(TAG, "1th scale operation dimenions - width: " + width + ",
height: " + height);
double y = Math.sqrt(IMAGE_MAX_SIZE
/ (((double) width) / height));
double x = (y / height) * width;
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, (int) x,
(int) y, true);
bitmap.recycle();
bitmap = scaledBitmap;
System.gc();
} else {
bitmap = BitmapFactory.decodeStream(in);
}
in.close();
Log.d(TAG, "bitmap size - width: " +bitmap.getWidth() + ", height: " +
bitmap.getHeight());
return bitmap;
} catch (IOException e) {
Log.e(TAG, e.getMessage(),e);
return null;
}
How to Manage Your App's Memory: link
如何管理应用程序的内存:链接
It's not a good idea to useandroid:largeHeap="true"
here's the extract from google that explains it,
使用android:largeHeap="true"
这里的谷歌摘录来解释它不是一个好主意,
However, the ability to request a large heap is intended only for a small set of apps that can justify the need to consume more RAM (such as a large photo editing app). Never request a large heap simply because you've run out of memory and you need a quick fix—you should use it only when you know exactly where all your memory is being allocated and why it must be retained. Yet, even when you're confident your app can justify the large heap, you should avoid requesting it to whatever extent possible. Using the extra memory will increasingly be to the detriment of the overall user experience because garbage collection will take longer and system performance may be slower when task switching or performing other common operations.
但是,请求大堆的能力仅适用于一小组可以证明需要消耗更多 RAM 的应用程序(例如大型照片编辑应用程序)。永远不要仅仅因为内存不足而需要快速修复而请求大堆——只有当您确切地知道所有内存被分配在哪里以及为什么必须保留它时,才应该使用它。然而,即使您确信您的应用程序可以证明大堆是合理的,您也应该尽可能避免请求它。使用额外的内存将越来越损害整体用户体验,因为在任务切换或执行其他常见操作时垃圾收集将花费更长的时间并且系统性能可能更慢。
After working excrutiatingly with out of memory errors
i would say adding this to the manifest to avoid the oom issue is not a sin
在与out of memory errors
我拼命工作后,我会说将其添加到清单以避免 oom 问题并不是一种罪过
Verifying App Behavior on the Android Runtime (ART)
在 Android 运行时 (ART) 上验证应用行为
The Android runtime (ART) is the default runtime for devices running Android 5.0 (API level 21) and higher. This runtime offers a number of features that improve performance and smoothness of the Android platform and apps. You can find more information about ART's new features in Introducing ART.
Android 运行时 (ART) 是运行 Android 5.0(API 级别 21)及更高版本的设备的默认运行时。此运行时提供了许多可提高 Android 平台和应用程序的性能和流畅度的功能。您可以在ART 简介 中找到有关 ART 新功能的更多信息。
However, some techniques that work on Dalvik do not work on ART. This document lets you know about things to watch for when migrating an existing app to be compatible with ART. Most apps should just work when running with ART.
但是,一些适用于 Dalvik 的技术不适用于 ART。本文档让您了解在迁移现有应用程序以与 ART 兼容时需要注意的事项。大多数应用程序在与 ART 一起运行时应该可以正常工作。
Addressing Garbage Collection (GC) Issues
解决垃圾收集 (GC) 问题
Under Dalvik, apps frequently find it useful to explicitly call System.gc() to prompt garbage collection (GC). This should be far less necessary with ART, particularly if you're invoking garbage collection to prevent GC_FOR_ALLOC-type occurrences or to reduce fragmentation. You can verify which runtime is in use by calling System.getProperty("java.vm.version"). If ART is in use, the property's value is "2.0.0" or higher.
在 Dalvik 下,应用程序经常发现显式调用 System.gc() 来提示垃圾回收 (GC) 很有用。对于 ART,这应该是不必要的,特别是如果您正在调用垃圾收集以防止 GC_FOR_ALLOC 类型的发生或减少碎片。您可以通过调用 System.getProperty("java.vm.version") 来验证正在使用哪个运行时。如果正在使用 ART,则该属性的值为“2.0.0”或更高。
Furthermore, a compacting garbage collector is under development in the Android Open-Source Project (AOSP) to improve memory management. Because of this, you should avoid using techniques that are incompatible with compacting GC (such as saving pointers to object instance data). This is particularly important for apps that make use of the Java Native Interface (JNI). For more information, see Preventing JNI Issues.
此外,Android 开源项目 (AOSP) 正在开发压缩垃圾收集器,以改进内存管理。因此,您应该避免使用与压缩 GC 不兼容的技术(例如保存指向对象实例数据的指针)。这对于使用 Java 本机接口 (JNI) 的应用程序尤其重要。有关更多信息,请参阅防止 JNI 问题。
Preventing JNI Issues
防止 JNI 问题
ART's JNI is somewhat stricter than Dalvik's. It is an especially good idea to use CheckJNI mode to catch common problems. If your app makes use of C/C++ code, you should review the following article:
ART 的 JNI 比 Dalvik 的要严格一些。使用 CheckJNI 模式来捕获常见问题是一个特别好的主意。如果您的应用程序使用 C/C++ 代码,您应该查看以下文章:
Also, you can use native memory (NDK& JNI), so you actually bypass the heap size limitation.
此外,您可以使用本机内存(NDK和JNI),因此您实际上可以绕过堆大小限制。
Here are some posts made about it:
以下是一些关于它的帖子:
and here's a library made for it:
这是一个为它制作的图书馆:
回答by Cativail
I see only two options:
我只看到两个选项:
- You have memory leaks in your application.
- Devices do not have enough memory when running your application.
- 您的应用程序中存在内存泄漏。
- 运行您的应用程序时,设备没有足够的内存。
回答by An-droid
You should implement an LRU cache manager when dealing with bitmap
处理位图时应该实现 LRU 缓存管理器
http://developer.android.com/reference/android/util/LruCache.htmlhttp://developer.android.com/training/displaying-bitmaps/cache-bitmap.htmlWhen should I recycle a bitmap using LRUCache?
http://developer.android.com/reference/android/util/LruCache.html http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html我什么时候应该使用 LRUCache 回收位图?
OR
或者
Use a tier library like Universal Image Loader :
使用像 Universal Image Loader 这样的层库:
https://github.com/nostra13/Android-Universal-Image-Loader
https://github.com/nostra13/Android-Universal-Image-Loader
EDIT :
编辑 :
Now when dealing with images and most of the time with bitmap I use Glide which let you configure a Glide Module and a LRUCache
现在在处理图像和大部分时间处理位图时,我使用 Glide,它可以让你配置一个 Glide 模块和一个 LRUCache
回答by Sandipkumar Savani
Few hints to handle such error/exception for Android Apps:
处理 Android 应用程序此类错误/异常的一些提示:
Activities & Application have methods like:
- onLowMemory
- onTrimMemory Handle these methods to watch on memory usage.
tag in Manifest can have attribute 'largeHeap' set to TRUE, which requests more heap for App sandbox.
Managing in-memory caching & disk caching:
- Images and other data could have been cached in-memory while app running, (locally in activities/fragment and globally); should be managed or removed.
Use of WeakReference, SoftReference of Java instance creation , specifically to files.
If so many images, use proper library/data structure which can manage memory, use samling of images loaded, handle disk-caching.
Handle OutOfMemory exception
Follow best practices for coding
- Leaking of memory (Don't hold everything with strong reference)
Minimize activity stack e.g. number of activities in stack (Don't hold everything on context/activty)
- Context makes sense, those data/instances not required out of scope (activity and fragments), hold them into appropriate context instead global reference-holding.
Minimize the use of statics, many more singletons.
Take care of OS basic memory fundametals
- Memory fragmentation issues
Involk GC.Collect() manually sometimes when you are sure that in-memory caching no more needed.
活动和应用程序具有以下方法:
- 低内存
- onTrimMemory 处理这些方法以观察内存使用情况。
Manifest 中的标签可以将属性“largeHeap”设置为 TRUE,这会为应用沙箱请求更多堆。
管理内存缓存和磁盘缓存:
- 图像和其他数据可以在应用程序运行时缓存在内存中(在活动/片段本地和全局中);应该被管理或移除。
使用 WeakReference,Java 实例创建的 SoftReference,专门针对文件。
如果图像太多,请使用可以管理内存的适当库/数据结构,使用加载的图像采样,处理磁盘缓存。
处理 OutOfMemory 异常
遵循编码的最佳实践
- 内存泄漏(不要用强引用来保存所有内容)
最小化活动堆栈,例如堆栈中的活动数量(不要将所有内容都放在上下文/活动中)
- 上下文是有道理的,那些不需要超出范围(活动和片段)的数据/实例,将它们保存在适当的上下文中,而不是全局引用保存。
尽量减少静态的使用,更多的单例。
照顾好操作系统的基本内存基础
- 内存碎片问题
当您确定不再需要内存缓存时,有时会手动调用 GC.Collect()。
回答by Gaurav Lambole
If you are getting this Error java.lang.OutOfMemoryError this is the most common problem occurs in Android. This error is thrown by the Java Virtual Machine (JVM) when an object cannot be allocated due to lack of memory space.
如果您收到此错误 java.lang.OutOfMemoryError,这是 Android 中最常见的问题。由于内存空间不足而无法分配对象时,Java 虚拟机 (JVM) 会抛出此错误。
Try this android:hardwareAccelerated="false" , android:largeHeap="true"
in your
manifest.xml file under application like this:
android:hardwareAccelerated="false" , android:largeHeap="true"
在应用程序下的 manifest.xml 文件中试试这个:
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:hardwareAccelerated="false"
android:largeHeap="true" />