Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bitmap recycle with largeHeap enabled

Before enabling largeHeap option, I was handling large bitmaps and it's consume almost the entire memory available for the application, and recycling it over navigation and loading new ones works round on almost the full heap available. However when some operations needs a bit more memory the application crashes. So I enabled largeHeap=true to have a bit more memory.

But doing this has a unexpected behavior, it's looks like that recycle() method of bitmaps do not work most of times, and the application that worked in 58Mb of memory (and exceeds sometimes throwing a OutOfMemoryException) now consumes memory exponentially and keeps growing (for now the test I did came to 231Mb allocated memory), the expected behavior is that the memory management keeps working and the application will not use more than 60Mb.

How can I avoid that? Or efficiently recycle bitmaps?

EDIT: Actually, I made it give a OutOfMemoryError when allocating more than 390Mb of memory on the device. Reading GC_* logs shown that only GC_FOR_ALLOC that freed 3.8Mb sometimes, but almost never other GC runs freed something.

like image 652
Marcos Vasconcelos Avatar asked Oct 03 '12 20:10

Marcos Vasconcelos


2 Answers

You should probably have a look at Displaying Bitmaps Efficiently which includes several ways to handle large Bitmaps Efficiently,

  • Loading Large Bitmaps Efficiently
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;

This will give you the size of the image before downloading and on that basis you can check the size of your device and scale it using calculateInSampleSize() and decodeSampledBitmapFromResource() given in the explanation of docs.

Calculating how much we need to scale the image,

  • First way
if (imageHeight > reqHeight || imageWidth > reqWidth) {
      if (imageWidth > imageHeight ) {
          inSampleSize = Math.round((float)imageHeight / (float)reqHeight);
      } else {
          inSampleSize = Math.round((float)imageWidth / (float)reqWidth);
      }
    }
  • Second way
int inSampleSize = Math.min(imageWidth / reqWidth,imageHeight / reqHeight);

The you can set the inSampleSize,

 options.inSampleSize = inSampleSize;

Then finally make sure you call,

options.inJustDecodeBounds = false;

else it will return Bitmap as null

  • Processing Bitmaps Off the UI Thread

    Processing Bitmap on UI thread is never safe so its always better to do that in a background thread and update UI after the process is completed.

  • Caching Bitmaps

    LruCache is available from API 12 but if you are interested it using below versions it is also available in Support Library too. So caching of Images should be done efficiently using that. Also you can use DiskLruCache for images where you want then to remain for longer period in extenal storage.

  • Clearing the Cache

    Sometimes when your image size is too large even caching the image causes OutOfMemoryError so in that case its better to clear the cache when your image is out of the scope or not used for longer period so that other images can be cached.

    I had created a demo example for the same, you can download from here

like image 132
Lalit Poptani Avatar answered Oct 01 '22 14:10

Lalit Poptani


Your case behaves as expected. Before Honeycomb, recycle() was unconditionally freeing the memory. But on 3.0 and above, bitmaps are part of normal garbage collected memory. You have plenty of RAM on the device, you allowed the JVM to allocate more than the 58M limit, now the garbage collector is satisfied and has no incentive to reclaim memory occupied by your bitmaps.

You can verify this by running on an emulator with controlled amount of RAM, or load some memory consuming service on your device - GC will jump to work. You can use DDMS to further investigate your memory usage.

You can try some solutions for bitmap memory management: Bitmaps in Android Bitmap memory leaks http://blog.javia.org/how-to-work-around-androids-24-mb-memory-limit/, but start with the official Android bitmap tips, as explained in @Lalit Poptani's detailed answer.

Note that moving the bitmaps to OpenGL memory as textures has some performance implications (but perfect if you will render these bitmaps through OpenGL in the end). Both textures and malloc solutions require that you explicitly free the bitmap memory which you don't use anymore.

like image 28
Alex Cohn Avatar answered Oct 01 '22 12:10

Alex Cohn