OutOfMemoryException

  • Antworten:9
Hans Meier
  • Forum-Beiträge: 12

15.11.2010, 14:49:54 via Website

Hallo ich habe ein Problem vom Erzeugen von Bildern.
Ich lade Bilder von einem Webserver und speicher sie auf einer SD-Karte.
Bei einer kleinen Anzahl von Bilder funktioniert das wunderbar, ab ca 50. Bildern bekomme ich einen OutOfMemoryError


Die Bilder haben eine Auflösung von 313x313 und sind in der Regel zwischen 15KB - 60KB groß.

Ich verstehe nicht, warum der Speicher überlauft?!?

Vielleicht hatte jemand schonmal so eine Situation...

URL = URL des Bildes
Path = der Pfad mit dem es auf der SD-Karte gespeichert wird
Source = von welcher Funktion der Aufruf kommt (optional)
Quality = Qualität beim erstellen des Bildes auf der SD-Karte (bm.compress)

1public Drawable createExternalStoragePrivateFile(String url, String path, int source, int quality) {
2
3 File file = null;
4 Bitmap bm = null;
5 Drawable d = null;
6
7
8 try {
9 file = new File(RealApplication.externalFilesDir, path);
10
11 } catch (NullPointerException e) {
12 e.printStackTrace();
13 Log.e("NULLPOINTEREXCEPTION", "new File");
14 return null;
15 }
16
17 if (!file.exists()) {
18
19 try {
20
21 Options options = new Options();
22 options.inPurgeable = true;
23
24 HttpGet httpRequest = null;
25 httpRequest = new HttpGet(url);
26 HttpClient httpclient = new DefaultHttpClient();
27 HttpResponse response = (HttpResponse) httpclient
28 .execute(httpRequest);
29 HttpEntity entity = response.getEntity();
30 BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity);
31 InputStream instream = bufHttpEntity.getContent();
32 bm = BitmapFactory.decodeStream(instream, null, options);
33 OutputStream os = new FileOutputStream(file);
34
35
36
37 bm.compress(CompressFormat.JPEG, quality, os);
38
39 bm.recycle();
40 d = Drawable.createFromPath(file.getPath());
41 os.close();
42 instream.close();
43
44 } catch (IOException e) {
45 // Unable to create file, likely because external storage is
46 // not currently mounted.
47 Log.e("ExternalStorage", "Error writing " + file, e);
48 } catch (NullPointerException e) {
49 Log.e("NULLPOINTEREXCEPTION", "in createExternal");
50 } catch (IllegalArgumentException e) {
51 Log.e("ILLEGALARGUMENTEXCEPTION", "in createExternal");
52 }
53
54
55 } else {
56 Log.i("INFO", "File already exists");
57 d = Drawable.createFromPath(file.getPath());
58
59 }
60
61 return d;
62
63 }




111-15 14:34:58.682: ERROR/dalvikvm-heap(4927): 195938-byte external allocation too large for this process.
211-15 14:34:58.682: ERROR/dalvikvm(4927): Out of memory: Heap Size=11143KB, Allocated=2908KB, Bitmap Size=13357KB
311-15 14:34:58.682: ERROR/GraphicsJNI(4927): VM won't let us allocate 195938 bytes
411-15 14:34:58.682: DEBUG/skia(4927): --- decoder->decode returned false
511-15 14:34:58.682: DEBUG/AndroidRuntime(4927): Shutting down VM
611-15 14:34:58.682: WARN/dalvikvm(4927): threadid=1: thread exiting with uncaught exception (group=0x400259f8)
711-15 14:34:58.691: ERROR/AndroidRuntime(4927): FATAL EXCEPTION: main
811-15 14:34:58.691: ERROR/AndroidRuntime(4927): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
911-15 14:34:58.691: ERROR/AndroidRuntime(4927): at android.graphics.BitmapFactory.nativeDecodeFile(Native Method)
1011-15 14:34:58.691: ERROR/AndroidRuntime(4927): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:287)
1111-15 14:34:58.691: ERROR/AndroidRuntime(4927): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:305)
1211-15 14:34:58.691: ERROR/AndroidRuntime(4927): at android.graphics.drawable.Drawable.createFromPath(Drawable.java:801)

— geändert am 15.11.2010, 14:51:19

Antworten
Mac Systems
  • Forum-Beiträge: 1.727

17.11.2010, 16:59:05 via Website

Zuerst mal ist das erzeugen des HTTPClients eine sache die exakt einmal gemacht werden sollte, nicht in einer funktion die mehrmals benutzt wird.
Ansonsten sehe ich ausser einiger unschönheiten im Code wenig was den OOM erklären könnte. Mittels eines Profilers kann man da aber sicher mehr zu sagen, das Android Plugin bietet diese Funktion ja bereits.

Windmate HD, See you @ IO 14 , Worked on Wundercar, Glass V3, LG G Watch, Moto 360, Android TV

Antworten
Hans Meier
  • Forum-Beiträge: 12

17.11.2010, 17:24:11 via Website

Welche Unschönheiten meinst du? Bin für Verbesserungsvorschläge offen.

Es hat sich herausgestellt, dass der OOM durch eine andere Ursache ausgelöst wird (Drawable.createFromPath(...))

Antworten
Tobias Eckert
  • Forum-Beiträge: 155

20.11.2010, 21:02:13 via Website

Die Speicherverwaltung für Bitmaps ist bei Android ein Thema für sich. Wenn Du eine Bitmap aus einer Datei erzeugst allokiert Android hier den vollen Speicher, unabhängig von der Dateigröße. D.h. wenn Du 313x313 Pixel hast mit 8 Bit Farbtiefe werden knapp 800k Speicher belegt, unabhängig davon wie groß die Quelldatei nun ist.

Du hast ja schon ein recycle() statement drin. Setz am besten die entsprechende Variable danach noch auf null, und triggere eine Garbage Collection via System.gc().

Ansonsten kannst Du auch versuchen per BitmapOptions die Grafiken gleich mit geringerer Farbtiefe erstellen zu lassen.

BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;

Antworten
Hans Meier
  • Forum-Beiträge: 12

22.11.2010, 09:49:38 via Website

Ich habe den code aktualisiert

1public void createExternalStoragePrivateFile(String url, String path, int source, int quality) {
2
3 File file = null;
4 Bitmap bm = null;
5 InputStream instream = null;
6
7
8 try {
9 file = new File(RealApplication.externalFilesDir, path);
10
11 } catch (NullPointerException e) {
12 e.printStackTrace();
13 Log.e("NULLPOINTEREXCEPTION", "new File");
14
15 }
16
17 if (!file.exists()) {
18
19 try {
20 Log.i("CREATE", "Quality: " + quality + " URL: " + url);
21
22 BitmapFactory.Options ops = new BitmapFactory.Options();
23 ops.inPurgeable = true;
24 ops.inTempStorage = new byte [8*1024];
25
26 HttpGet httpRequest = null;
27 httpRequest = new HttpGet(url);
28 HttpClient httpclient = new DefaultHttpClient();
29 HttpResponse response = (HttpResponse) httpclient
30 .execute(httpRequest);
31 HttpEntity entity = response.getEntity();
32 BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity);
33 instream = bufHttpEntity.getContent();
34 bm = BitmapFactory.decodeStream(instream, null, ops);
35 OutputStream os = new FileOutputStream(file);
36
37
38 bm.compress(CompressFormat.JPEG, quality, os);
39
40 }
41
42 clearBitmap(bm);
43 os.close();
44 instream.close();
45
46
47 } catch (IOException e) {
48 // Unable to create file, likely because external storage is
49 // not currently mounted.
50 Log.e("ExternalStorage", "Error writing " + file, e);
51 } catch (NullPointerException e) {
52 Log.e("NULLPOINTEREXCEPTION", "in createExternal");
53 } catch (IllegalArgumentException e) {
54 Log.e("ILLEGALARGUMENTEXCEPTION", "in createExternal");
55 }
56
57
58 } else {
59 Log.i("INFO", "File already exists");
60
61 }
62
63
64
65 }
66
67
68 public void clearBitmap(Bitmap bm) {
69
70 bm.recycle();
71
72 bm = null;
73
74 System.gc();
75 }

Die Drawables werden jetzt an anderer stelle mit folgenden Einstellungen erstellt.

BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 4;

Antworten
Marcel S.
  • Forum-Beiträge: 120

22.02.2011, 10:29:07 via Website

Gibt es denn eine Möglichkeit die Bitmaps zu laden ohne das der volle Speicher allokiert wird???

Zum Beispiel über Bitmap.Compress().

Antworten
Mac Systems
  • Forum-Beiträge: 1.727

22.02.2011, 10:46:34 via Website

Das wäre dann eine Thumbnail Ansicht oder was stellst du dir vor ?

Windmate HD, See you @ IO 14 , Worked on Wundercar, Glass V3, LG G Watch, Moto 360, Android TV

Antworten
Marcel S.
  • Forum-Beiträge: 120

23.02.2011, 09:42:29 via Website

Ich frage mich ob man den Bitmap Typ irgendwie Komprimieren kann ohne größeren Quallitätsverlust und bei gleichbleibender Dimensionierung.

Um z.B zu vermeiden das ein 800x800 pixel großes Bild das in der Datei 100kb groß ist dann im Bitmap geladen über 1Mb Ram verschling.

Ich denke z.B an Bitmap arrays für Animationen die einem ja schnell mal den speicher zum überlaufen bringen können wenn man sich gar so sehr künstlerisch austoben möchte :).

Entschuldige wenn das ne dämliche Frage sein sollte ich bin nicht der Vollprofie was das an geht :)

Antworten
Mac Systems
  • Forum-Beiträge: 1.727

23.02.2011, 11:19:59 via Website

Dimensionen sind hier das was du wissen musst: Also X*Y*4(32 Bit) = Bytes, was die Datei auf der Platte/SD Karte verbraucht ist nebensache und liegt z.b an der JPEG Komprimierung. Du kannst also selbst recht schnell ausrechnen wann dein Speicher knapp wird. Mann kann Bilder auch als 16 Bit laden, dann hast du aber Dihering und keinen Alpha Kanal (meine ich zumindest).

— geändert am 23.02.2011, 12:47:30

Windmate HD, See you @ IO 14 , Worked on Wundercar, Glass V3, LG G Watch, Moto 360, Android TV

Antworten
Marcel S.
  • Forum-Beiträge: 120

24.02.2011, 07:40:28 via Website

Ok dann muss ich mir was anderes einfallen lassen wenn ich mal ne größere Animation habe :)

Ich nahm an das Android alle Bilder grundsätzlich mit 16 bit läd.

Antworten