Frage Ein Problem mit nicht ausreichendem Speicher beim Laden eines Bildes in ein Bitmap-Objekt


Ich habe eine Listenansicht mit ein paar Bildknöpfen in jeder Zeile. Wenn Sie auf die Listenzeile klicken, wird eine neue Aktivität gestartet. Ich musste meine eigenen Tabs wegen eines Problems mit dem Kamera-Layout erstellen. Die Aktivität, die für das Ergebnis gestartet wird, ist eine Karte. Wenn ich auf meine Schaltfläche klicke, um die Bildvorschau zu starten (ein Bild von der SD - Karte zu laden), kehrt die Anwendung von der Aktivität zurück zur listview Aktivität zum Ergebnis-Handler, um meine neue Aktivität neu zu starten, die nichts anderes als ein Bild-Widget ist.

Die Bildvorschau in der Listenansicht erfolgt mit dem Cursor und ListAdapter. Das macht es ziemlich einfach, aber ich bin mir nicht sicher, wie ich ein Bild in der Größe anpassen kann (z. B. kleinere Bitgröße, nicht Pixel als src für den Bildknopf im laufenden Betrieb. Also habe ich nur das Bild verkleinert, das von der Telefonkamera kam.

Das Problem ist, dass ich einen nicht ausreichenden Speicherfehler erhalte, wenn es versucht, die zweite Aktivität erneut zu starten.

  • Gibt es eine Möglichkeit, den Listenadapter einfach Zeile für Zeile zu erstellen, wo ich die Größe ändern kann (ein bisschen weise)

Dies wäre vorzuziehen, da ich auch einige Änderungen an den Eigenschaften der Widgets / Elemente in jeder Zeile vornehmen muss, da ich aufgrund des Fokusproblems keine Zeile mit Touchscreen auswählen kann. (Ich kann Rollkugel benutzen.)

  • Ich weiß, dass ich eine Out-of-Band-Größenanpassung vornehmen und mein Bild speichern kann, aber das ist nicht wirklich das, was ich machen möchte, aber ein Beispielcode wäre schön.

Sobald ich das Bild in der Listenansicht deaktiviert hatte, funktionierte es wieder gut.

FYI: So habe ich es gemacht:

String[] from = new String[] { DBHelper.KEY_BUSINESSNAME,DBHelper.KEY_ADDRESS,DBHelper.KEY_CITY,DBHelper.KEY_GPSLONG,DBHelper.KEY_GPSLAT,DBHelper.KEY_IMAGEFILENAME  + ""};
int[] to = new int[] {R.id.businessname,R.id.address,R.id.city,R.id.gpslong,R.id.gpslat,R.id.imagefilename };
notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);

Woher R.id.imagefilename ist ein ButtonImage.

Hier ist mein LogCat:

01-25 05:05:49.877: ERROR/dalvikvm-heap(3896): 6291456-byte external allocation too large for this process.
01-25 05:05:49.877: ERROR/(3896): VM wont let us allocate 6291456 bytes
01-25 05:05:49.877: ERROR/AndroidRuntime(3896): Uncaught handler: thread main exiting due to uncaught exception
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:304)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:149)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:174)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.drawable.Drawable.createFromPath(Drawable.java:729)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.resolveUri(ImageView.java:484)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.setImageURI(ImageView.java:281)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:183)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:129)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.CursorAdapter.getView(CursorAdapter.java:150)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.obtainView(AbsListView.java:1057)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.makeAndAddView(ListView.java:1616)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.fillSpecific(ListView.java:1177)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.layoutChildren(ListView.java:1454)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.onLayout(AbsListView.java:937)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1108)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:922)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:999)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:920)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.performTraversals(ViewRoot.java:771)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1103)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Handler.dispatchMessage(Handler.java:88)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Looper.loop(Looper.java:123)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.app.ActivityThread.main(ActivityThread.java:3742)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invokeNative(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invoke(Method.java:515)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at dalvik.system.NativeStart.main(Native Method)
01-25 05:10:01.127: ERROR/AndroidRuntime(3943): ERROR: thread attach failed 

Ich habe auch einen neuen Fehler beim Anzeigen eines Bildes:

01-25 22:13:18.594: DEBUG/skia(4204): xxxxxxxxxxx jpeg error 20 Improper call to JPEG library in state %d
01-25 22:13:18.604: INFO/System.out(4204): resolveUri failed on bad bitmap uri: 
01-25 22:13:18.694: ERROR/dalvikvm-heap(4204): 6291456-byte external allocation too large for this process.
01-25 22:13:18.694: ERROR/(4204): VM won't let us allocate 6291456 bytes
01-25 22:13:18.694: DEBUG/skia(4204): xxxxxxxxxxxxxxxxxxxx allocPixelRef failed

1105


Ursprung


Antworten:


Das Android-Training Klasse, "Bitmaps effizient anzeigen", bietet einige großartige Informationen für das Verständnis und den Umgang mit der Ausnahme java.lang.OutOfMemoryError: bitmap size exceeds VM budget beim Laden von Bitmaps.


Lesen Sie die Bitmap-Dimensionen und den Typ

Das BitmapFactory Klasse bietet mehrere Decodierungsmethoden (decodeByteArray(), decodeFile(), decodeResource()usw.) zum Erstellen eines Bitmap aus verschiedenen Quellen. Wählen Sie anhand Ihrer Bilddatenquelle die am besten geeignete Dekodierungsmethode aus. Diese Methoden versuchen, Speicher für die konstruierte Bitmap zuzuweisen und können daher leicht zu einem Ergebnis führen OutOfMemory Ausnahme. Jede Art von Dekodiermethode verfügt über zusätzliche Signaturen, mit denen Sie Dekodierungsoptionen über die Methode angeben können BitmapFactory.Options Klasse. Einstellen der inJustDecodeBounds Eigentum zu true während Decodierung vermeidet Speicherzuweisung, zurück null für das Bitmap-Objekt aber Einstellung outWidth, outHeight und outMimeType. Mit dieser Technik können Sie die Abmessungen und den Typ der Bilddaten vor der Erstellung (und Speicherzuordnung) der Bitmap lesen.

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

Vermeiden java.lang.OutOfMemory überprüfen Sie vor der Dekodierung die Abmessungen einer Bitmap, es sei denn, Sie vertrauen absolut darauf, dass die Quelle Ihnen Bilddaten mit einer vorhersehbaren Größe zur Verfügung stellt, die bequem in den verfügbaren Speicher passen.


Laden Sie eine verkleinerte Version in den Speicher

Jetzt, da die Bilddimensionen bekannt sind, können sie verwendet werden, um zu entscheiden, ob das vollständige Bild in den Speicher geladen werden soll oder ob stattdessen eine unterabgetastete Version geladen werden soll. Hier sind einige Faktoren zu beachten:

  • Geschätzte Speicherbelegung beim Laden des gesamten Bildes im Speicher.
  • Die Größe des Arbeitsspeichers, den Sie für das Laden dieses Abbilds bei anderen Speicheranforderungen Ihrer Anwendung bereitstellen möchten.
  • Dimensionen der Ziel-ImageView- oder UI-Komponente, in die das Bild geladen werden soll.
  • Bildschirmgröße und -dichte des aktuellen Geräts.

Beispielsweise lohnt es sich nicht, ein 1024x768-Pixel-Bild in den Speicher zu laden, wenn es schließlich in einem 128x96-Pixel-Thumbnail angezeigt wird ImageView.

Um dem Decoder zu sagen, dass er das Bild untersampling und eine kleinere Version in den Speicher lädt, legen Sie fest inSampleSize zu true in deinem BitmapFactory.Options Objekt. Zum Beispiel ein Bild mit der Auflösung 2048x1536, das mit einem .decodiert wird inSampleSize von 4 erzeugt eine Bitmap von ungefähr 512x384. Das Laden in den Speicher verwendet 0,75 MB anstelle von 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 basierend auf einer Zielbreite und -höhe eine Potenz von zwei ist:

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

Hinweis: Ein Zweierpotenzwert wird berechnet, weil der Decoder a verwendet   Endwert durch Abrundung auf die nächste Potenz von zwei, wie bei der    inSampleSize Dokumentation.

Um diese Methode zu verwenden, dekodieren Sie zuerst mit inJustDecodeBounds einstellen true, geben Sie die Optionen durch und dekodieren Sie sie erneut mit dem neuen Befehl 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 macht es einfach, ein Bitmap beliebiger Größe in ein zu laden ImageView Das zeigt ein 100x100 Pixel Thumbnail an, 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.


558



Um den OutOfMemory-Fehler zu beheben, sollten Sie Folgendes tun:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap preview_bitmap = BitmapFactory.decodeStream(is, null, options);

Dies inSampleSize Option reduziert den Speicherverbrauch.

Hier ist eine vollständige Methode. Zuerst liest es Bildgröße, ohne den Inhalt selbst zu dekodieren. Dann findet es das Beste inSampleSize Wert, sollte es eine Potenz von 2 sein, und schließlich wird das Bild decodiert.

// Decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
    try {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f), null, o);

        // The new size we want to scale to
        final int REQUIRED_SIZE=70;

        // Find the correct scale value. It should be the power of 2.
        int scale = 1;
        while(o.outWidth / scale / 2 >= REQUIRED_SIZE && 
              o.outHeight / scale / 2 >= REQUIRED_SIZE) {
            scale *= 2;
        }

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
    } catch (FileNotFoundException e) {}
    return null;
}

856



Ich habe eine kleine Verbesserung für Fedors Code gemacht. Es macht im Grunde dasselbe, aber ohne die (meiner Meinung nach) hässliche while-Schleife und es ergibt sich immer eine Zweierpotenz. Kudos an Fedor für die ursprüngliche Lösung, ich war stecken geblieben, bis ich seine gefunden hatte, und dann war ich in der Lage, dies zu machen :)

 private Bitmap decodeFile(File f){
    Bitmap b = null;

        //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;

    FileInputStream fis = new FileInputStream(f);
    BitmapFactory.decodeStream(fis, null, o);
    fis.close();

    int scale = 1;
    if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
        scale = (int)Math.pow(2, (int) Math.ceil(Math.log(IMAGE_MAX_SIZE / 
           (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
    }

    //Decode with inSampleSize
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize = scale;
    fis = new FileInputStream(f);
    b = BitmapFactory.decodeStream(fis, null, o2);
    fis.close();

    return b;
}

352



Ich komme aus iOS-Erfahrung und war frustriert, ein Problem mit etwas so Grundlegendem wie dem Laden und dem Zeigen eines Bildes zu entdecken. Schließlich versucht jeder, der dieses Problem hat, Bilder mit einer vernünftigen Größe anzuzeigen. Wie auch immer, hier sind die zwei Änderungen, die mein Problem behoben haben (und meine App sehr reaktionsfähig gemacht haben).

1) Jedes Mal, wenn Sie es tun BitmapFactory.decodeXYZ(), vergewissere dich, dass du ein passierst BitmapFactory.Options mit inPurgeable einstellen true (und vorzugsweise mit inInputShareable auch eingestellt auf true).

2) NIEMALS benutzen Bitmap.createBitmap(width, height, Config.ARGB_8888). Ich meine NIEMALS! Ich hatte nie, dass das Ding nach einigen Pässen keinen Speicherfehler aufwies. Keine Menge von recycle(), System.gc()Was auch immer geholfen hat. Es hat immer Ausnahme ausgelöst. Die eine andere Art und Weise, die tatsächlich funktioniert, ist ein Dummy-Bild in Ihrem Drawables zu haben (oder ein anderes Bitmap, die Sie 1 oben mit Schritt decodiert), neu skalieren, dass, was auch immer Sie wollen, dann manipulieren, um die resultierende Bitmap (wie es vorbei an einer Leinwand auf für mehr Spaß). Also, was Sie stattdessen verwenden sollten, ist: Bitmap.createScaledBitmap(srcBitmap, width, height, false). Wenn du aus irgendeinem Grund die Brute-Force-Create-Methode verwenden musst, dann pass mindestens auf Config.ARGB_4444.

Dies ist fast garantiert, um Sie Stunden, wenn nicht Tage zu sparen. All das Gerede über das Skalieren des Bildes usw. funktioniert nicht wirklich (es sei denn, Sie ziehen eine falsche Größe oder ein verschlechtertes Bild in Betracht).


220



Es ist ein bekannter FehlerEs liegt nicht an großen Dateien. Da Android die Drawables zwischenspeichert, geht der Speicher nach der Verwendung von wenigen Bildern aus. Aber ich habe einen alternativen Weg dafür gefunden, indem ich das Standard-Cache-System von Android überspringe.

Lösung: Verschieben Sie die Bilder in den Ordner "assets" und verwenden Sie die folgende Funktion, um BitmapDrawable zu erhalten:

public static Drawable getAssetImage(Context context, String filename) throws IOException {
    AssetManager assets = context.getResources().getAssets();
    InputStream buffer = new BufferedInputStream((assets.open("drawable/" + filename + ".png")));
    Bitmap bitmap = BitmapFactory.decodeStream(buffer);
    return new BitmapDrawable(context.getResources(), bitmap);
}

84



Ich hatte das gleiche Problem und löste es, indem ich die BitmapFactory.decodeStream- oder decodeFile-Funktionen vermeide und stattdessen verwendete BitmapFactory.decodeFileDescriptor

decodeFileDescriptor sieht so aus, als ob es andere native Methoden als decodeStream / decodeFile aufruft.

Wie auch immer, was funktioniert hat, war dies (beachten Sie, dass ich einige Optionen hinzugefügt habe, wie einige oben hatten, aber das war nicht der Unterschied. Was entscheidend ist, ist der Aufruf an BitmapFactory.decodeFileDescriptor Anstatt von decodeStream oder Dekodefile):

private void showImage(String path)   {
    Log.i("showImage","loading:"+path);
    BitmapFactory.Options bfOptions=new BitmapFactory.Options();
    bfOptions.inDither=false;                     //Disable Dithering mode
    bfOptions.inPurgeable=true;                   //Tell to gc that whether it needs free memory, the Bitmap can be cleared
    bfOptions.inInputShareable=true;              //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
    bfOptions.inTempStorage=new byte[32 * 1024]; 


    File file=new File(path);
    FileInputStream fs=null;
    try {
        fs = new FileInputStream(file);
    } catch (FileNotFoundException e) {
        //TODO do something intelligent
        e.printStackTrace();
    }

    try {
        if(fs!=null) bm=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
    } catch (IOException e) {
        //TODO do something intelligent
        e.printStackTrace();
    } finally{ 
        if(fs!=null) {
            try {
                fs.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    //bm=BitmapFactory.decodeFile(path, bfOptions); This one causes error: java.lang.OutOfMemoryError: bitmap size exceeds VM budget

    im.setImageBitmap(bm);
    //bm.recycle();
    bm=null;



}

Ich denke, es gibt ein Problem mit der nativen Funktion, die in decodeStream / decodeFile verwendet wird. Ich habe bestätigt, dass eine andere native Methode aufgerufen wird, wenn Sie decodeFileDescriptor verwenden. Ich habe auch gelesen, dass Bilder (Bitmaps) nicht auf eine Standard-Java-Weise, sondern über native Aufrufe zugewiesen werden, die Zuordnungen erfolgen außerhalb des virtuellen Heap, sind aber zählte dagegen!"


69



Ich denke, der beste Weg, das zu vermeiden OutOfMemoryError ist es zu stellen und es zu verstehen.

Ich habe eine gemacht App absichtlich verursachen OutOfMemoryErrorund überwachen die Speichernutzung.

Nachdem ich viele Experimente mit dieser App gemacht habe, habe ich folgende Schlussfolgerungen:

Ich werde zuerst über SDK-Versionen vor Honey Comb sprechen.

  1. Bitmap wird im nativen Heap gespeichert, aber es wird automatisch Müll gesammelt, der Aufruf von recycle () ist überflüssig.

  2. Wenn {VM-Heap-Größe} + {zugeordnet nativen Heap-Speicher}> = {VM Heap-Größenbegrenzung für das Gerät}, und Sie versuchen, Bitmap zu erstellen, wird OOM geworfen werden.

    HINWEIS: VM HEAP SIZE wird gezählt und nicht VM ALLOCATED MEMORY.

  3. Die VM-Heap-Größe wird nach der Vergrößerung niemals verkleinert, selbst wenn der zugewiesene VM-Speicher verkleinert wird.

  4. Sie müssen also den VM-Spitzenspeicher so niedrig wie möglich halten, damit die VM-Heap-Größe nicht zu groß wird, um den verfügbaren Speicher für Bitmaps zu sparen.

  5. Manuell nennen System.gc () ist bedeutungslos, das System wird es zuerst aufrufen, bevor Sie versuchen, die Größe des Heap zu vergrößern.

  6. Native Heap-Größe wird auch nicht verkleinert, aber sie wird nicht für OOM gezählt. Sie müssen sich also keine Gedanken darüber machen.

Lassen Sie uns dann über SDK Starts von Honey Comb sprechen.

  1. Bitmap wird im VM-Heap gespeichert, der native Speicher wird nicht für OOM gezählt.

  2. Die Bedingung für OOM ist viel einfacher: {VM-Heap-Größe}> = {VM-Heap-Größenbeschränkung für das Gerät}.

  3. Sie haben also mehr verfügbaren Speicher zum Erstellen von Bitmap mit der gleichen Heap-Größenbeschränkung. Daher ist es weniger wahrscheinlich, dass OOM ausgelöst wird.

Hier sind einige meiner Beobachtungen zu Garbage Collection und Memory Leak.

Du kannst es selbst in der App sehen. Wenn eine Aktivität eine AsyncTask ausgeführt hat, die nach dem Löschen der Aktivität noch ausgeführt wurde, wird für die Aktivität kein Garbage Collection-Vorgang ausgeführt, bis die AsyncTask beendet wird.

Dies liegt daran, dass AsyncTask eine Instanz einer anonymen inneren Klasse ist und eine Referenz der Aktivität enthält.

Der Aufruf von AsyncTask.cancel (true) stoppt die Ausführung nicht, wenn die Aufgabe in einer IO-Operation im Hintergrund-Thread blockiert wird.

Callbacks sind auch anonyme innere Klassen. Wenn also eine statische Instanz in Ihrem Projekt sie enthält und nicht freigibt, würde Speicher verloren gehen.

Wenn Sie eine sich wiederholende oder verzögerte Aufgabe planen, z. B. einen Timer, und Sie cancel () und purge () nicht in onPause () aufrufen, wird Speicher ausgelagert.


64