4

I have read 100s of article about the OOM problem. Most are in regard to large bitmaps. I am doing a mapping application where we download 256x256 weather overlay tiles. Most are totally transparent and very small. I just got a crash on a bitmap stream that was 442 Bytes long while calling BitmapFactory.decodeByteArray(....).

The Exception states:

java.lang.OutOfMemoryError: bitmap size exceeds VM budget(Heap Size=9415KB, Allocated=5192KB, Bitmap Size=23671KB)

The code is:

protected Bitmap retrieveImageData() throws IOException {
    URL url = new URL(imageUrl);
    InputStream in = null;
    OutputStream out = null;
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();

    // determine the image size and allocate a buffer
    int fileSize = connection.getContentLength();
    if (fileSize < 0) {
        return null;
    }
    byte[] imageData = new byte[fileSize];

    // download the file
    //Log.d(LOG_TAG, "fetching image " + imageUrl + " (" + fileSize + ")");
    BufferedInputStream istream = new BufferedInputStream(connection.getInputStream());
    int bytesRead = 0;
    int offset = 0;
    while (bytesRead != -1 && offset < fileSize) {
        bytesRead = istream.read(imageData, offset, fileSize - offset);
        offset += bytesRead;
    }

    // clean up
    istream.close();
    connection.disconnect();
    Bitmap bitmap = null;
    try {
        bitmap = BitmapFactory.decodeByteArray(imageData, 0, bytesRead);
    } catch (OutOfMemoryError e) {
        Log.e("Map", "Tile Loader (241) Out Of Memory Error " + e.getLocalizedMessage());
        System.gc();
    }
    return bitmap;

}

Here is what I see in the debugger:

bytesRead = 442

So the Bitmap data is 442 Bytes. Why would it be trying to create a 23671KB Bitmap and running out of memory?

Murtaza Khursheed Hussain
  • 15,176
  • 7
  • 58
  • 83
Bob Keathley
  • 301
  • 2
  • 5
  • 6
  • Things to watch out that some OOM doesn't caused necessarily by the page that is triggering the error. At times, the OOM is triggered by accumulation from previous activities/bugs that get triggered by Bitmap manipulation by chance. We should look the app as a whole instead of just the place that triggered the OOM. A recent SO question that I just answered illustrated this scenario http://stackoverflow.com/questions/7136198/android-many-outofmemoryerror-exceptions-only-on-single-activity-with-mapview/7154643#7154643 where the OOM is not necessarily caused by the Activity that threw the error. – momo Sep 03 '11 at 04:04
  • There are already many answers related to this question on SO, here is one of the solution that should work for you. [Memory exceeds](http://stackoverflow.com/questions/477572/android-strange-out-of-memory-issue/823966#823966) **UPDATED:** Another nice answer is here, [Memory Leaks](http://mobi-solutions.blogspot.com/2010/08/how-to-if-you-want-to-create-and.html) – Lalit Poptani Sep 03 '11 at 04:27

3 Answers3

2

I have run into problems like this in the past. Android uses Bitmap VM and it is very small. Make sure you dispose your bitmap via bmp.recycle. Later versions of Android have more Bitmap VM but the version that I've been dealing with has a 20MB limit.

Flexo
  • 87,323
  • 22
  • 191
  • 272
Kenny Lim
  • 1,193
  • 1
  • 11
  • 31
  • I have done as much of that as I can. This is drawing about 12-18 of these small bitmaps. The largest is about 10K. So I have made it as skinny as it can be. It is 442 bytes that is being expanded to 20+MB. This is on Android 2.3 – Bob Keathley Sep 03 '11 at 04:14
  • Folks also this is a routine to cache Tile images for drawing on a MapView. I cannot recycle the bitmaps or the draw routines on the map will crash. – Bob Keathley Sep 03 '11 at 16:10
0

This may work. Shrinking the bitmaps to lesser quality. I am not sure, but this may duplicate the image in memory, but could easily be worth a shot.

            Bitmap image;
   image = BitmapFactory.decodeByteArray(data, 0, data.length);
   Bitmap mutableBitmap = image.copy(Bitmap.Config.ARGB_4444, true);
   Canvas canvas = new Canvas(mutableBitmap); 

My old answer below I don't think will work streaming.

                Options options = new BitmapFactory.Options();
        Options options2 = new BitmapFactory.Options();
        Options options3 = new BitmapFactory.Options();

        options.inPreferredConfig = Bitmap.Config.ARGB_8888;///////includes alpha
        options2.inPreferredConfig = Bitmap.Config.RGB_565 ;///////no alpha
        options3.inPreferredConfig = Bitmap.Config.ARGB_4444 ;/////alpha lesser quality

        image=BitmapFactory.decodeResource(getResources(),R.drawable.imagename,options); 
        image=Bitmap.createScaledBitmap(image, widthx,height, true);  
a54studio
  • 965
  • 11
  • 11
0

It sounds like you've already done some reading on this subject, so I'll spare you the standard 'this is how you decode a bitmap' comments..

It jumps out at me that you're perhaps holding on to a reference of old bitmaps (perhaps the tile has moved off screen, but you still have a reference in an array somewhere and as a result it isn't being garbage collected?). This has bitten me really badly in the past - memory leaks are hard to debug.

There's a great Google I/O video over here that really helped me when I was having similar problems. It's around an hour, but will hopefully save you days later on. It covers things like:

  1. Creating heap dumps
  2. Heap usage in DDMS
  3. Using MAT to compare/analyze heap dumps
Ben Pearson
  • 7,532
  • 4
  • 30
  • 50