programing

Android에서 java.lang.OutOfMemoryError 문제를 해결하는 방법

nasanasas 2020. 11. 20. 09:03
반응형

Android에서 java.lang.OutOfMemoryError 문제를 해결하는 방법


드로어 블 폴더에 매우 작은 크기의 이미지가 있지만 사용자로부터이 오류가 발생합니다. 그리고 코드에서 비트 맵 기능을 사용하지 않습니다. 적어도 의도적으로 :)

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$9$1.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)

이 stackTrace에 따르면이 줄 에서이 오류가 발생합니다 ( 'tv'는 textView입니다).

tv.setBackgroundResource(R.drawable.yanlis);

무엇이 문제입니까? 코드에 대한 다른 정보가 필요하면 추가 할 수 있습니다. 감사!


힙 크기를 동적으로 늘릴 수는 없지만 사용하여 더 많이 사용하도록 요청할 수 있습니다.

android : largeHeap = "true"

에서 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">

애플리케이션의 프로세스를 큰 Dalvik 힙으로 생성해야하는지 여부입니다. 이는 응용 프로그램에 대해 생성 된 모든 프로세스에 적용됩니다. 프로세스에로드 된 첫 번째 응용 프로그램에만 적용됩니다. 공유 사용자 ID를 사용하여 여러 응용 프로그램이 프로세스를 사용할 수 있도록 허용하는 경우 모두이 옵션을 일관되게 사용해야합니다. 그렇지 않으면 예측할 수없는 결과가 발생합니다. 대부분의 앱은이 기능이 필요하지 않으며 성능 향상을 위해 전체 메모리 사용량을 줄이는 데 초점을 맞춰야합니다. 일부 장치는 사용 가능한 총 메모리에 의해 제한되기 때문에이 기능을 활성화해도 사용 가능한 메모리의 고정 증가가 보장되지 않습니다.


런타임에 사용 가능한 메모리 크기를 쿼리하려면 getMemoryClass()또는 getLargeMemoryClass().

여전히 문제가 발생하면 이것도 작동합니다.

 BitmapFactory.Options options = new BitmapFactory.Options();
 options.inSampleSize = 8;
 mBitmapInsurance = BitmapFactory.decodeFile(mCurrentPhotoPath,options);

값> 1로 설정하면 디코더에 원본 이미지를 서브 샘플링하도록 요청하여 메모리를 절약하기 위해 더 작은 이미지를 반환합니다.

이것은 이미지 표시 속도와 관련하여 BitmapFactory.Options.inSampleSize를 최적으로 사용하는 것입니다. 문서는 2의 거듭 제곱 인 값을 사용하는 것을 언급하므로 2, 4, 8, 16 등으로 작업하고 있습니다.

이미지 샘플링에 대해 더 자세히 살펴 보겠습니다.

예를 들어 1024x768 픽셀 이미지가 결국 .NET 파일의 128x128 픽셀 축소판으로 표시되는 경우 메모리에로드 할 가치가 없습니다 ImageView.

메모리 설정에 작은 버전을로드 이미지를 표본을 디코더에게 inSampleSizetrue당신의 BitmapFactory.Options객체입니다. 예를 들어 해상도가 2100 x 1500 픽셀 inSampleSize이고를 4로 디코딩 한 이미지 는 약 512x384의 비트 맵을 생성합니다. 이것을 메모리에로드하면 전체 이미지에 대해 12MB가 아닌 0.75MB를 사용합니다 (비트 맵 구성을라고 가정 ARGB_8888). 다음은 대상 너비와 높이를 기준으로 2의 거듭 제곱 인 샘플 크기 값을 계산하는 방법입니다.

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;
}

참고 : 디코더는 inSampleSize문서 에 따라 가장 가까운 2의 거듭 제곱으로 반올림하여 최종 값을 사용하기 때문에 2의 거듭 제곱이 계산 됩니다.

이 방법을 사용하려면 먼저로 inJustDecodeBounds설정하여 디코딩 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);
}

이 메서드를 사용 ImageView하면 다음 예제 코드와 같이 100x100 픽셀 축소판을 표시하는 에 임의로 큰 비트 맵을 쉽게로드 할 수 있습니다.

mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

유사한 프로세스에 BitmapFactory.decode*따라 필요에 따라 적절한 방법으로 대체하여 다른 소스의 비트 맵을 디코딩 할 수 있습니다 .


이 코드도 흥미 롭다는 것을 알았습니다.

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;
}

앱의 메모리를 관리하는 방법 : 링크


사용하는 것은 좋은 생각이 아닙니다. android:largeHeap="true" 여기에 설명하는 Google의 발췌 내용이 있습니다.

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.

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


Verifying App Behavior on the Android Runtime (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.

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.


Addressing Garbage Collection (GC) Issues

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.

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.


Preventing JNI Issues

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:


Also, you can use native memory (NDK & JNI), so you actually bypass the heap size limitation.

Here are some posts made about it:

and here's a library made for it:


I see only two options:

  1. You have memory leaks in your application.
  2. Devices do not have enough memory when running your application.

You should implement an LRU cache manager when dealing with bitmap

http://developer.android.com/reference/android/util/LruCache.html http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html When should I recycle a bitmap using LRUCache?

OR

Use a tier library like 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

https://github.com/bumptech/glide


Few hints to handle such error/exception for Android Apps:

  1. Activities & Application have methods like:

    • onLowMemory
    • onTrimMemory Handle these methods to watch on memory usage.
  2. tag in Manifest can have attribute 'largeHeap' set to TRUE, which requests more heap for App sandbox.

  3. 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.
  4. Use of WeakReference, SoftReference of Java instance creation , specifically to files.

  5. If so many images, use proper library/data structure which can manage memory, use samling of images loaded, handle disk-caching.

  6. Handle OutOfMemory exception

  7. Follow best practices for coding

    • Leaking of memory (Don't hold everything with strong reference)
  8. 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.
  9. Minimize the use of statics, many more singletons.

  10. Take care of OS basic memory fundametals

    • Memory fragmentation issues
  11. Involk GC.Collect() manually sometimes when you are sure that in-memory caching no more needed.


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.

Try this android:hardwareAccelerated="false" , android:largeHeap="true"in your manifest.xml file under application like this:

<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" />

참고URL : https://stackoverflow.com/questions/25719620/how-to-solve-java-lang-outofmemoryerror-trouble-in-android

반응형