So lösen Sie java.lang.OutOfMemoryError-Probleme in Android

Lesezeit: 12 Minuten

Obwohl ich ein sehr kleines Bild im Drawable-Ordner habe, erhalte ich diesen Fehler von Benutzern. Und ich verwende keine Bitmap-Funktion im Code. Zumindest gewollt 🙂

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)

Laut diesem StackTrace erhalte ich diesen Fehler in dieser Zeile (‘tv’ ist eine TextView):

tv.setBackgroundResource(R.drawable.yanlis);

Was ist das Problem? Wenn Sie weitere Informationen zum Code benötigen, kann ich diese hinzufügen. Danke!

  • Hast du das gelesen? developer.android.com/training/displaying-bitmaps/…

    – 2Dee

    8. September 2014 um 7:56 Uhr

  • Versuchen Sie Folgendes: stackoverflow.com/questions/19558713/…

    – Schlampig

    8. September 2014 um 8:00 Uhr

  • Nein, aber wie gesagt, ich habe ein sehr kleines Bild (max. 600 KB). Ich denke, das ist für ein größeres Bild. @2Dee

    – Utku Soytaş

    8. September 2014 um 8:00 Uhr


So losen Sie javalangOutOfMemoryError Probleme in Android
Maveňツ

Sie können die Heap-Größe nicht dynamisch erhöhen, aber Sie können anfordern, mehr zu verwenden, indem Sie verwenden.

android:largeHeap=”true”

in dem manifest.xmlkönnen Sie diese Zeilen in Ihr Manifest einfügen, es funktioniert in einigen Situationen.

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:largeHeap="true"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

Ob die Prozesse Ihrer Anwendung mit einem großen Dalvik-Heap erstellt werden sollen. Dies gilt für alle Prozesse, die für die Anwendung erstellt werden. Es gilt nur für die erste Anwendung, die in einen Prozess geladen wird; Wenn Sie eine gemeinsame Benutzer-ID verwenden, um mehreren Anwendungen die Verwendung eines Prozesses zu ermöglichen, müssen alle diese Option konsistent verwenden, da sie sonst zu unvorhersehbaren Ergebnissen führen. Die meisten Apps sollten dies nicht benötigen und sich stattdessen darauf konzentrieren, ihre Gesamtspeichernutzung zu reduzieren, um die Leistung zu verbessern. Die Aktivierung garantiert auch keine feste Erhöhung des verfügbaren Speichers, da einige Geräte durch ihren insgesamt verfügbaren Speicher eingeschränkt sind.


Um die verfügbare Speichergröße zur Laufzeit abzufragen, verwenden Sie die Methoden getMemoryClass() oder getLargeMemoryClass().

Wenn immer noch ein Problem auftritt, sollte dies auch funktionieren

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

Wenn auf einen Wert > 1 gesetzt, wird der Decoder aufgefordert, das Originalbild zu unterabtasten und ein kleineres Bild zurückzugeben, um Speicherplatz zu sparen.

Dies ist die optimale Verwendung von BitmapFactory.Options.inSampleSize im Hinblick auf die Anzeigegeschwindigkeit des Bildes. Die Dokumentation erwähnt die Verwendung von Werten, die eine Potenz von 2 sind, also arbeite ich mit 2, 4, 8, 16 usw.

Lassen Sie uns tiefer in das Bild-Sampling einsteigen:

Es lohnt sich beispielsweise nicht, ein 1024 x 768 Pixel großes Bild in den Speicher zu laden, wenn es schließlich in einem 128 x 128 Pixel großen Thumbnail in einem angezeigt wird ImageView.

Um den Decoder anzuweisen, das Bild zu unterabtasten, indem eine kleinere Version in den Speicher geladen wird, set inSampleSize zu true in deiner BitmapFactory.Options Objekt. Zum Beispiel ein Bild mit einer Auflösung von 2100 x 1500 Pixel, das mit dekodiert wird inSampleSize von 4 erzeugt eine Bitmap von ungefähr 512 x 384. Das Laden in den Speicher verwendet 0,75 MB statt 12 MB für das vollständige Bild (unter der Annahme einer Bitmap-Konfiguration von ARGB_8888). Hier ist eine Methode zum Berechnen eines Stichprobengrößenwerts, der eine Potenz von zwei ist, basierend auf einer Zielbreite und -höhe:

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

Notiz: Ein Zweierpotenzwert wird berechnet, da der Decoder einen Endwert verwendet, indem er gemäß der auf die nächste Zweierpotenz abrundet
inSampleSize Dokumentation.

Um diese Methode zu verwenden, decodieren Sie zuerst mit inJustDecodeBounds einstellen trueübergeben Sie die Optionen und dekodieren Sie dann erneut mit dem neuen inSampleSize Wert und inJustDecodeBounds einstellen 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);
}

Diese Methode erleichtert das Laden einer beliebig großen Bitmap in eine ImageView das ein 100 x 100 Pixel großes Thumbnail anzeigt, wie im folgenden Beispielcode gezeigt:

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

Sie können einem ähnlichen Prozess folgen, um Bitmaps aus anderen Quellen zu decodieren, indem Sie das entsprechende ersetzen BitmapFactory.decode* Methode nach Bedarf.


Ich fand diesen Code auch interessant:

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

So verwalten Sie den Speicher Ihrer App: Verknüpfung


Es ist keine gute Idee, es zu verwenden android:largeHeap="true" Hier ist der Auszug von Google, der es erklärt,

Die Möglichkeit, einen großen Heap anzufordern, ist jedoch nur für eine kleine Gruppe von Apps gedacht, die die Notwendigkeit rechtfertigen können, mehr RAM zu verbrauchen (z. B. eine große Fotobearbeitungs-App). Fordern Sie niemals einen großen Heap an, nur weil Ihnen der Arbeitsspeicher ausgeht und Sie eine schnelle Lösung benötigen – Sie sollten ihn nur verwenden, wenn Sie genau wissen, wo Ihr gesamter Arbeitsspeicher zugewiesen wird und warum er beibehalten werden muss. Doch selbst wenn Sie sicher sind, dass Ihre App den großen Haufen rechtfertigen kann, sollten Sie es so weit wie möglich vermeiden, sie anzufordern. Die Verwendung des zusätzlichen Speichers wird sich zunehmend nachteilig auf die allgemeine Benutzererfahrung auswirken, da die Garbage Collection länger dauert und die Systemleistung beim Wechseln von Aufgaben oder beim Ausführen anderer gängiger Vorgänge möglicherweise langsamer ist.

Nach der Arbeit unerträglich mit out of memory errors Ich würde sagen, dass es keine Sünde ist, dies dem Manifest hinzuzufügen, um das Oom-Problem zu vermeiden


Überprüfen des App-Verhaltens auf der Android-Laufzeit (ART)

Die Android-Laufzeit (ART) ist die Standardlaufzeit für Geräte mit Android 5.0 (API-Ebene 21) und höher. Diese Laufzeit bietet eine Reihe von Funktionen, die die Leistung und Laufruhe der Android-Plattform und -Apps verbessern. Weitere Informationen zu den neuen Funktionen von ART finden Sie in Wir stellen KUNST vor.

Einige Techniken, die bei Dalvik funktionieren, funktionieren jedoch nicht bei ART. In diesem Dokument erfahren Sie, worauf Sie achten müssen, wenn Sie eine vorhandene App migrieren, damit sie mit ART kompatibel ist. Die meisten Apps sollten nur funktionieren, wenn sie mit ART ausgeführt werden.


Behandlung von Problemen mit der Garbage Collection (GC).

Unter Dalvik finden es Apps häufig nützlich, System.gc() explizit aufzurufen, um die Garbage Collection (GC) anzufordern. Dies sollte bei ART weitaus weniger notwendig sein, insbesondere wenn Sie die Garbage Collection aufrufen, um Vorkommen vom Typ GC_FOR_ALLOC zu verhindern oder die Fragmentierung zu reduzieren. Sie können überprüfen, welche Laufzeit verwendet wird, indem Sie System.getProperty(“java.vm.version”) aufrufen. Wenn ART verwendet wird, ist der Wert der Eigenschaft “2.0.0” oder höher.

Darüber hinaus wird im Android Open-Source Project (AOSP) ein kompaktierender Garbage Collector entwickelt, um die Speicherverwaltung zu verbessern. Aus diesem Grund sollten Sie die Verwendung von Techniken vermeiden, die mit der Komprimierung von GC nicht kompatibel sind (z. B. das Speichern von Zeigern auf Objektinstanzdaten). Dies ist besonders wichtig für Apps, die das Java Native Interface (JNI) verwenden. Weitere Informationen finden Sie unter Verhindern von JNI-Problemen.


Verhindern von JNI-Problemen

Die JNI von ART ist etwas strenger als die von Dalvik. Es ist eine besonders gute Idee, den CheckJNI-Modus zu verwenden, um allgemeine Probleme zu erkennen. Wenn Ihre App C/C++-Code verwendet, sollten Sie den folgenden Artikel lesen:


Sie können auch nativen Speicher verwenden (NDK & JNI), sodass Sie die Heap-Größenbeschränkung tatsächlich umgehen.

Hier sind einige Beiträge dazu entstanden:

  • So cachen Sie Bitmaps im nativen Speicher

  • https://stackoverflow.com/a/9428660/1761003

  • JNI-Bitmap-Operationen , um OOM bei der Verwendung großer Bilder zu vermeiden

und hier ist eine Bibliothek dafür gemacht:

  • Ihre android:largeHeap=”true” Tricks helfen mir sehr. Vielen Dank Herr

    – Md. Sajedul Karim

    4. Juli 2015 um 17:58 Uhr

  • @Fakher hat den anderen Link gelesen, Sir … Hier sind einige Beiträge dazu: – stackoverflow.com/questions/17900732/… – stackoverflow.com/questions/18250951/… und hier ist eine dafür erstellte Bibliothek: – github.com/AndroidDeveloperLB/AndroidJniBitmapOperations

    – Maveňツ

    9. September 2015 um 8:54 Uhr


  • Gut gesagt Zeilen Gattsu: "For example, it’s not worth loading a 1024x768 pixel image into memory if it will eventually be displayed in a 128x96 pixel thumbnail in an ImageView."

    – Sam

    17. Juli 2017 um 12:00 Uhr


  • Dies ist eine der vollständigsten und besten Antworten, die ich gelesen habe

    – Benutzer-ID0908

    11. Juli 2018 um 11:24 Uhr

Ich sehe nur zwei Möglichkeiten:

  1. Sie haben Speicherlecks in Ihrer Anwendung.
  2. Geräte verfügen nicht über genügend Arbeitsspeicher, wenn Ihre Anwendung ausgeführt wird.

  • Wie kann ich Speicherlecks in meiner App beheben?

    – Utku Soytaş

    8. September 2014 um 8:01 Uhr

  • Durch Erkennen der Orte, die viele Daten generieren und speichern (normalerweise liegt es an dem Problem, dass die Bilder nicht freigegeben werden). Möglicherweise finden Sie dies hilfreich: stackoverflow.com/questions/2298208/…

    – Cativail

    8. September 2014 um 8:07 Uhr


Wenn Sie diesen Fehler java.lang.OutOfMemoryError erhalten, ist dies das häufigste Problem, das in Android auftritt. Dieser Fehler wird von der Java Virtual Machine (JVM) ausgegeben, wenn ein Objekt aufgrund von Speicherplatzmangel nicht zugeordnet werden kann.

Versuche dies android:hardwareAccelerated="false" , android:largeHeap="true"in Ihrer manifest.xml-Datei unter Anwendung wie folgt:

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

  • Warum ich machen sollte android:hardwareAccelerated="false"? Wenn ich es wahr mache, was passiert dann?

    – Prinz Dholakiya

    30. Mai 2019 um 10:43 Uhr


  • Wenn Sie hardwareAccelerated=”false” schreiben, erhalten Sie in Ihrem Projekt keine Erhöhung für Cardview, denken Sie darüber nach?

    – dileep krishnan

    30. August 2019 um 13:01 Uhr

So losen Sie javalangOutOfMemoryError Probleme in Android
Android

Sie sollten einen LRU-Cache-Manager implementieren, wenn Sie mit Bitmaps arbeiten

http://developer.android.com/reference/android/util/LruCache.html
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
Wann sollte ich eine Bitmap mit LRUCache recyceln?

ODER

Verwenden Sie eine Tier-Bibliothek wie Universal Image Loader :

https://github.com/nostra13/Android-Universal-Image-Loader

BEARBEITEN :

Wenn ich mich jetzt mit Bildern und meistens mit Bitmaps befasse, verwende ich Glide, mit dem Sie ein Glide-Modul und einen LRUCache konfigurieren können

https://github.com/bumptech/glide

Einige Hinweise zum Umgang mit solchen Fehlern/Ausnahmen für Android-Apps:

  1. Aktivitäten & Anwendung haben Methoden wie:

    • onLowMemory
    • onTrimMemory Behandeln Sie diese Methoden, um die Speichernutzung zu überwachen.
  2. -Tag im Manifest kann das Attribut „largeHeap“ auf TRUE gesetzt sein, wodurch mehr Heap für die App-Sandbox angefordert wird.

  3. Verwalten von In-Memory-Caching und Disk-Caching:

    • Bilder und andere Daten könnten während der Ausführung der App im Arbeitsspeicher zwischengespeichert worden sein (lokal in Aktivitäten/Fragmenten und global); verwaltet oder entfernt werden sollen.
  4. Verwendung von WeakReference, SoftReference der Java-Instanzerstellung, speziell für Dateien.

  5. Wenn so viele Bilder vorhanden sind, verwenden Sie die richtige Bibliothek/Datenstruktur, die den Speicher verwalten kann, verwenden Sie das Sampling der geladenen Bilder und handhaben Sie das Disk-Caching.

  6. OutOfMemory-Ausnahme behandeln

  7. Befolgen Sie Best Practices für die Codierung

    • Speicherlecks (Halte nicht alles mit starker Referenz)
  8. Aktivitätsstapel minimieren, z. B. Anzahl der Aktivitäten im Stapel (Halten Sie nicht alles auf Kontext/Aktivität)

    • Kontext macht Sinn, diese Daten/Instanzen, die nicht außerhalb des Geltungsbereichs benötigt werden (Aktivität und Fragmente), halten sie in einem angemessenen Kontext, anstatt globale Referenzen zu halten.
  9. Minimieren Sie die Verwendung von Statics, viel mehr Singletons.

  10. Achten Sie auf die grundlegenden Speichergrundlagen des Betriebssystems

    • Probleme mit der Speicherfragmentierung
  11. Rufen Sie GC.Collect() manchmal manuell auf, wenn Sie sicher sind, dass das In-Memory-Caching nicht mehr benötigt wird.

  • Können Sie bitte den Code teilen, wie die OutOfMemory-Ausnahme behandelt wird

    – Chetan Chaudhari

    23. April 2019 um 11:27 Uhr

android:largeHeap="true" hat den Fehler nicht behoben

In meinem Fall habe ich diesen Fehler erhalten, nachdem ich ein Symbol/Bild zum Drawable-Ordner hinzugefügt habe, indem ich SVG in Vektor konvertiert habe. Gehen Sie einfach zur Symbol-XML-Datei und stellen Sie kleine Zahlen für die Breite und Höhe ein

android:width="24dp"
android:height="24dp"
android:viewportWidth="3033"
android:viewportHeight="3033"

  • Können Sie bitte den Code teilen, wie die OutOfMemory-Ausnahme behandelt wird

    – Chetan Chaudhari

    23. April 2019 um 11:27 Uhr

1646251391 529 So losen Sie javalangOutOfMemoryError Probleme in Android
Damecy

Überprüfen Sie die Bildgröße

Ich habe ein ~ 350-kB-Bild in eine Bildansicht direkt über XML geladen (app:srcCompat), was zu einem OOM-Fehler führte und die Anwendung abstürzte.

Um es zu lösen, habe ich genau dasselbe Bild mit Glide in dieselbe Bildansicht geladen und es hat funktioniert!

Lektion: Bildgröße reduzieren / Laden des Bildes verzögern

915640cookie-checkSo lösen Sie java.lang.OutOfMemoryError-Probleme in Android

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy