OutOfMemory

  • Antworten:8
mcfly
  • Forum-Beiträge: 286

06.10.2011, 08:36:19 via Website

Hallo zusammen

Ich habe ein Problem, das erst 2-mal aufgetreten ist ( DeveloperConsole ) und ich bei mir nicht nachvollziehen kann.

Beim decodieren eines JPG's erhalte ich einen MemoryError, weil ich vorgängig schon andere JPG's decodiert habe. Der Fehler kommt einfach, weil das Gerät einfach zuwenig Memory hatte ( Behaupte ich mal :-) ). -> java.lang.OutOfMemoryError: bitmap size exceeds VM budget(Heap Size=10439KB, Allocated=7056KB, Bitmap Size=10183KB
Meine Idee war, da es sehr selten vorkommt, einfach eine Exception darum zu bauen und ein leeres Bild, das ich sowieso schon geladen habe, anzuzeigen.

Trotzdem gabs den Fehler bei einer neueren Installation. Habt Ihr mir einen Tip, wie ich das handeln könnte ?

1try{
2 bm = BitmapFactory.decodeFile(sImage);
3 }catch (Exception ex){
4 bm=bmleer;
5 }

Grüsse

Antworten
Rafael K.
  • Forum-Beiträge: 2.359

06.10.2011, 08:46:25 via Website

Es gibt in Java verschiedene Fehlerklassen.
Exceptions sind Ausnahmezustände, die man behandeln kann, weil die gesamte Applikation dadurch nicht beeinträchtigt wird.
Errors sind Fehler, die die Lauffähigkeit der gesamten VM in Frage stellen und NICHT GEFANGEN werden sollten.

Wenn ein OutOfMemoryError auftritt, können bereits mehrere Speicheranforderungen ins Leere gelaufen sein und Objekte, von denen du erwartest, dass die gültig sind, können ins Leere zeigen ... usw, usw. Die VM ist in einem undefinierten Zustand.

Du solltest dir besser überlegen, ob du die Größe des zu ladenden Bildes vorher ermitteln kannst und ausprobieren wie groß ein Bild als Datei sein kann, um Menge X an Speicher zu benötigen. Dann enscheidest du VOR dem Laden, ob der Speicher noch reicht.

Kleiner Tipp noch am Rande:
Teste das mal mit einem Emulator. Beim Galaxy z.B. ist der maximale Heap auf 48MB pro App eingestellt. Standard ist 16. D.h. der Fehler wird eher auf den schwachbrüstigeren Geräten auftauchen, denn auf den High-End-Modellen.

Antworten
mcfly
  • Forum-Beiträge: 286

06.10.2011, 09:03:10 via Website

Vielen Dank für die ausführliche Antwort. Ich habs befürchtet, dass ich da mehr tun muss :-)

Die Size des Images kann ich herausfinden. Wie kann ich aber herausfinden, wieviel Memory meine App ( heap ) noch frei hat.
Es gibt ActivityManager.getMemoryInfo(ActivityManager.MemoryInfo). . So kann man anscheindend herausfinden wieviel Memory das System noch hat. Bin ich da richtg ?

Grüsse

Antworten
Rafael K.
  • Forum-Beiträge: 2.359

06.10.2011, 09:13:00 via Website

Also ich hab das damals, als ich ein Speicherleck gesucht habe, mit den Standard Java Methoden gemacht.
Wenn es da in Android noch was besseres gibt...nimm evtl. das :)

Sonst:
http://download.oracle.com/javase/6/docs/api/java/lang/Runtime.html

freeMemory, maxMemory und totalMemory

Wobei man wissen muss, dass die Heap Größe bei Bedarf bis auf maxMemory ansteigen darf,
totalMemory den aktuellen verbrauchten Speicher ausgibt und freeMemory den freien Speicher in der aktuellen Größe der VM.

Also wenn 16 das Maximum ist, die VM aber mangels Bedarf erst 8 groß ist, wovon 7 belegt sind, wird freeMemory 1 liefern.
Interessant ist also maxMemory - totalMemory.

— geändert am 06.10.2011, 09:13:30

Antworten
Gelöschter Account
  • Forum-Beiträge: 294

06.10.2011, 10:04:29 via Website

Diese Bitmap Error haben mich in allen Projekten immer wieder begleitet. Ich nutze derzeit mehrere Strategien:

* Handelt es sich um Images die ich nicht kenne und von denen ich erwarten muss das sie etwas größer sein werden (z:B. in meiner Gallery for Wuala App) dann missbrauche ich ein WebView Widget zur Anzeige. Solange es sich um die reine Anzeige handelt nimmt einem dieses Widget alles ab.

* Handelt es sich um Images die ich kenne und deren Größe im Thumbnail Bereich liegen dann verwende ich einen eigenen DrawableCache zur Verwaltung und füge der App eine extended Application bei. Dort fange ich die Memory Hinweise ab und veranlasse den DrawableCache sich zu shrinken bzw. zu löschen. Auch in dem DrawableCache ist eine maximale Obergrenze so dass dort jederzeit der Cache verkleinert werden kann. Das ImageView wird von mir hierzu extended und erhält ein Member zur Aufnahme der Ressource-Id. Das Nachladen erledigt dann der DrawableCache selbständig wenn ein Image gezeichnet werden soll und das Drawable aus dem Cache entfernt wurde.

* Kenne ich die Images und deren Größen und es handelt sich um fette Bilder dann werden diese von mir mit Hilfe der Factory kleiner skaliert.

Wenn man sich nicht sicher ist dann würde ich jedem das WebView zur Anzeige empfehlen. Man hat dann zwar nur rudimentäre Touchscreen Features aber für die Anzeige reicht das.

Gruß
Harald

Antworten
mcfly
  • Forum-Beiträge: 286

06.10.2011, 15:57:48 via Website

Vielen vielen Dank für eure Antworten.

Ich versuche mal den Ansatz mit der Runtime.freememory .

Da ich weiss, dass meine Bilder nie grösser als 50kb ( 100kb als if-Abfrage ) sind, habe ich folgendes gemacht, was denkt ihr:

1try{
2 Long lfreememory=runtime.freeMemory();
3//Log.v("lfreememory : ", String.valueOf(lfreememory/1000) + " kb.");
4 if (lfreememory>100000){
5 bm = BitmapFactory.decodeFile(getsImagePath() + sImageFile);
6 }else{
7 bm=bmna; // Bitmap, das Anzeigen soll, dass es nicht verfügbar ist ( na= Not available ).
8 }
9 }catch (Exception ex){
10 bm=bmna;
11 }

— geändert am 06.10.2011, 15:59:10

Antworten
mcfly
  • Forum-Beiträge: 286

07.10.2011, 08:30:28 via Website

Danke für die gute Erklärung und Du hattest es ja eigentlich schon erklärt mit "Wobei man wissen muss, dass die Heap Größe bei Bedarf bis auf maxMemory ansteigen darf".

Jetzt ist also mein Code. Bitte nochmals um kurzes Review.

1try{
2 Long lTotalMemory=runtime.totalMemory();
3 Long lMaxMemory=runtime.maxMemory();
4//Log.v("lfreememory : ", String.valueOf(lfreememory/1000) + " kb.");
5 if (lMaxMemory-lTotalMemory>100000){
6 bm = BitmapFactory.decodeFile(getsImagePath() + sImageFile);
7 }else{
8 bm=bmna;
9 }
10 }catch (Exception ex){
11 bm=bmna;
12 }

Antworten
Rafael K.
  • Forum-Beiträge: 2.359

07.10.2011, 08:44:46 via Website

Jupp, so ists recht :)
Mit der Grenze von 100000 musst du halt mal ein bisschen rumspielen und ausprobieren...was so ein Bild tatsächlich braucht.
Gib den totalMemory aus, lade ein Bild, gib totalMemory nochmal aus.
Dann hast du den ungefähren Speicherverbrauch und solltest das mit einem Sicherheitspolster als Grenze nehmen.

Ansonsten finde ich die Ansätze von Harald sehr interessant.
Würde ich auch mal testen. Klingt jedenfalls eleganter als diese Lösung :)

Antworten