it-swarm.com.de

Android: OutofMemoryError: Die Bitmapgröße überschreitet VM Budget ohne Grund kann ich sehen

Ich habe eine OutOfMemory-Ausnahme mit einer Galerie mit über 600 x 800 Pixel großen JPEGs.


Die Umgebung

Ich habe eine Galerie mit JPG-Bildern mit 600x800 Pixeln verwendet.

Da mein Inhalt möglicherweise etwas komplexer als nur Bilder ist, habe ich jede Ansicht als RelativeLayout festgelegt, die ImageView mit dem JPG umschließt.

Um die Benutzererfahrung zu "beschleunigen", habe ich einen einfachen Cache mit 4 Slots, der ungefähr 1 Bild links und 1 Bild rechts auf das angezeigte Bild (in einem Looper) vorausholt und sie in einem 4 Slot HashMap speichert. 

Die Platform

Ich verwende AVD von 256 RAM und 128 Heap Size mit einem 600x800-Bildschirm. Dies geschieht auch auf einem Entourage Edge-Ziel, außer dass mit dem Gerät das Debuggen schwieriger ist.


Das Problem

Ich habe eine Ausnahme bekommen:

OutofMemoryError: bitmap size exceeds VM budget

Und es passiert beim Abrufen des fünften Bildes. Ich habe versucht, die Größe meines Bild-Caches zu ändern, und er ist immer noch gleich.


Das Merkwürdige: Es sollte kein Speicherproblem geben

Um sicherzustellen, dass das Heap-Limit sehr weit von dem entfernt ist, was ich brauche, habe ich am Anfang ein Dummy-8-MB-Array definiert, das nicht referenziert wurde, sodass es sofort ausgelöst wird. Es ist ein Mitglied des Aktivitätsthreads und wird wie folgt definiert

static { @SuppressWarnings("unused")
byte dummy[] = new byte[ 8*1024*1024 ]; }    

Das Ergebnis ist, dass die Heap-Größe fast 11 MB beträgt und alles frei ist. Hinweis Ich habe diesen Trick hinzugefügt, nachdem er abgestürzt war. Dadurch wird OutOfMemory weniger häufig.

Jetzt verwende ich DDMS. Kurz vor dem Absturz (ändert sich nicht viel nach dem Absturz), zeigt DDMS:

ID  Heap Size   Allocated   Free       %Used    #Objects
1   11.195 MB   2.428 MB    8.767 MB   21.69%   47,156  

Und in der Detailtabelle zeigt es:

Type  Count  Total Size   Smallest   Largest   Median    Average
free  1,536  8.739MB      16B        7.750MB   24B       5.825KB

Der größte Block beträgt 7,7 MB. Und doch sagt das LogCat:

ERROR/dalvikvm-heap(1923): 925200-byte external allocation too large for this process.

Wenn Sie die Beziehung zwischen dem Median und dem Durchschnitt beachten, ist es plausibel anzunehmen, dass die meisten verfügbaren Blöcke sehr klein sind. Es gibt jedoch einen Block, der groß genug für die Bitmap ist, nämlich 7,7 MB. Wie kommt es noch nicht genug?

Hinweis: Ich habe eine Heap-Spur aufgezeichnet. Bei der Betrachtung der zugeteilten Datenmenge scheint es nicht so, als würden mehr als 2 Mio. zugewiesen. Es stimmt mit dem Bericht über den freien Speicher von DDMS überein.


  • Könnte es sein, dass ich ein Problem wie Haufen-Fragmentierung habe?
  • Wie löse ich das Problem?
  • Ist der Heap für alle Threads freigegeben?
  • Könnte es sein, dass ich die DDMS-Anzeige falsch interpretiere und es wirklich keinen 900K-Block gibt, der zugewiesen werden kann? Wenn ja, kann mir bitte jemand sagen, wo ich das sehen kann?

Danke vielmals

Meymann

34
Meymann

Ich denke, es gibt nichts Besonderes in Ihrem Fall. Es gibt einfach nicht genug Speicher. Sie können nicht mehrere 600x800-Bitmaps im Speicher haben, sie verbrauchen zu viel Speicher. Sie sollten sie in SD speichern und bei Bedarf in den Speicher laden. Ich denke, genau das tust du.

Eine Sache, die Sie beachten sollten: DDMS zeigt den Java-Heap-Speicherverbrauch an. Es gibt jedoch auch nativen Speicher, der in DDMS nicht angezeigt wird. Und so weit ich weiß, werden Bitmaps im nativen Speicher erstellt. Daher ist DDMS nur ein schlechtes Werkzeug, um diese Speicherprobleme zu verfolgen. Sie müssen nur sicherstellen, dass Sie Ihr Gedächtnis freigeben, dass Bilder von Garbage Collector erfasst werden, nachdem Sie sie nicht mehr benötigen.

Garbage Collector arbeitet nach eigenem Zeitplan. Deshalb sollten Sie die Bitmap.recycle () -Methode für Bitmaps aufrufen, die Sie nicht mehr benötigen. Diese Methode gibt genau den nativen Speicher frei, aus dem Sie ausgehen. Auf diese Weise sind Sie nicht auf GC angewiesen und können so schnell wie möglich den größten Speicherplatz freigeben.

Zunächst sollten Sie sicherstellen, dass Sie keine Bitmaps durchlaufen.

Hier ist ein Nizza post auf Speicherzuordnungen, es kann Ihnen helfen, tiefer zu graben 

12
Fedor

Sie sind sich nicht sicher, ob es eine Option für Sie ist, aber haben Sie versucht, Supersampling-Bilder zu erstellen Beim Laden eines Bildes in ein Bitmap-Objekt ein ungewöhnlicher Arbeitsspeicher aufgetreten ist ?

5
Fedor

Die Frage wurde 2010 gestellt, als Froyo frisch war. So viele Dinge sind seit. Passiert. Vor 3.0 wurden Bitmaps in JNI zugewiesen. Die Erinnerung zeigte sich nicht in den Statistiken von Dalvik. Es muss nicht mehr monolithisch sein. Vor 2.3 waren keine Speicherstatistiken für JNI verfügbar (Bitmap-Dekodierung ruft JNI auf) in logcat. 4.4 evakuiert mehr Speicherplatz. 5.0 der große Knall von Art. Im Jahr 2010 war Nexus One mit weniger als 300 MB ein Spitzenwert. Das Budget für eine App betrug rund 16 MB. Heute ist das Gedächtnis etwa achtmal so groß.

0
Meymann

Ich hatte auch vor ein paar Wochen ein ähnliches Problem, und ich löste es, indem ich die Bilder auf den optimalen Punkt herunter skalierte. Ich habe in meinem Blog hier einen vollständigen Ansatz geschrieben und ein komplettes Beispielprojekt mit OOM-Code anfällig gegen OOM-Proof-Code hier hochgeladen.

Es ist viel Zeit vergangen, seit ich das gefragt habe.

Die Antwort sollte in zwei Teile unterteilt sein: Pre-Gingerbread: Sie verwenden nur kleine Bilder, verwenden Subsampling, möglicherweise ein Bild in Bildschirmgröße, und hoffen auf etwas Gutes. Stellen Sie sicher, dass Sie keine winzigen Elemente zuweisen, die Sie nicht freigeben können, bevor Sie eine Bitmap erhalten. Vor Ginger musste der Speicher für BMPs kontinuierlich sein, und er wurde nicht im Speicher VM gezählt. Schauen Sie sich immer das Logcat an. Sehen Sie einen Vortrag über das Gedächtnis von Google IO 2011. Post Ginger ist einfacher. Seit Honeycomb werden Bitmaps sogar in Ihrem Java-Bereich gezählt. Es gibt keinen jni-Bereich. Verwenden Sie immer recycle für Bitmaps, die Sie nicht benötigen. Warten Sie nicht auf den GC.

0
Meymann