When (if at all) should I use Bitmap.recycle()? - android

According to Android Developers site, the Bitmap.recycle() method definition is:
Free the native object associated with this bitmap, and clear the reference to the pixel data
I've developed some applications which are creating / decoding a lot of bitmaps, and put the result bitmap objects to ImageViews. Sometimes I've got the famous exceptions such as:
bitmap size excceded vm budget
and
out of memory error
Also I'm sure I don't have any memory leaks that can cause that.
After a lot of searches, I discoverd the "recycle" method, and used it to free the bitmap's native memory when no longer needed. It seems like it helped a lot.
I'm asking if that's something I'm supposed to do on this situation, because
I know the system is doing this anyway without calling it explicitly (is it? maybe I'm wrong).
Should I use this method in situations like this?
In what situations should I use this method?
Should I use this method at all?
thanks in advance.
UPDATE:
google posted this guide recently, which says:
On Android 2.3.3 (API level 10) and lower, using recycle() is recommended. If you're displaying large amounts of bitmap data in your app, you're likely to run into OutOfMemoryError errors. The recycle() method allows an app to reclaim memory as soon as possible.

in what situations should I use this method?
The Bitmaps are GC'ed by GC whenever it decides.But in some situations it may get delayed.
And always remember thumb rule in java (Maybe it applies to othe P.L also).The speed of recycling objects by GC may not be same as speed of creating objects.So sometimes the GC is slow to in recycling.
so recycle() means If you want to free memory ASAP you should call recycle()
should I use this method at all??
This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap.But if you are facing the issues like bitmap size exceeded vm budget or out of memory error then you need to use this.

I do use it in operations where i know that bitmap is not going to be used anymore.
public static Bitmap getMaskedContactImage (Context context, Bitmap contactImageBitmap, int maskToBeApplied) {
Bitmap mask = BitmapFactory.decodeResource(context.getResources(), maskToBeApplied);
Bitmap output = Bitmap.createBitmap(mask.getWidth(),mask.getHeight(), Config.ARGB_8888);
final Rect finalRect = new Rect(0, 0, contactImageBitmap.getWidth(), contactImageBitmap.getHeight());
final Rect originRect = new Rect(0, 0, mask.getWidth(), mask.getHeight());
Canvas canvas = new Canvas(output);
Paint xferPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
xferPaint.setColor(Color.BLACK);
xferPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
canvas.drawBitmap(contactImageBitmap, finalRect, originRect, null);
canvas.drawBitmap(mask, originRect, originRect, xferPaint);
contactImageBitmap.recycle();
mask.recycle();
return output;
}
In places like that one, im sure im not going to use the mask or the contactImage.
I found a really good resource for Bitmap processing that can be helpfull Displaying bitmaps.
Regards,
Alex

Related

Out Of Memory Error in Android Studio

I'm trying to create my first android app, so I'm really new to android development. I'm trying to make a nice background image for my app. I made an image that is the exact dimensions of the device, but when I try to load it, i get the error "Failed to allocate a 218748 byte allocation with 217220 free bytes and 212KB until OOM" Here's my code:
#Override
protected void onDraw(Canvas canvas) {
drawBackground(canvas);
protected void drawBackground(Canvas canvas) {
Bitmap background = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.background2));
canvas.drawBitmap(background, 0, 0, null);
I've tried scaling it down, which works, but then it doesn't fill the entire screen. How do I resolve this issue?
You should recycle bitmap after canvas.drawBitmap(background, 0, 0, null);, or create bitmap once, in the constructor, and then use it, it is very costly to create bitmap each time in the onDraw method, it even costly to create simple objects in the onDraw, you shuold avoid from it, android:largeHeap="true" and System.gc() can't help in this case. Read this
Custom Drawing
Creating objects ahead of time is an important optimization. Views are redrawn very frequently, and many drawing objects require expensive initialization. Creating drawing objects within your onDraw() method significantly reduces performance and can make your UI appear sluggish.
There is blog that will help you to detect and prevent Out of memory issue. I wrote this based on a real problem I faced and here is the link
If you got stuck anywhere in the tutorial please let me know we can discuss and if you want to add something please suggest.

Should I trust the Garbage Collector after calling Bitmap.recycle()?

I have some code which is loading an image into an OpenGL texture. In the process, I end up loading 3 bitmaps, since I need to load the original bitmap (sized appropriately for the display) and reorient the bitmap based on EXIF data. I'm very quickly calling .recycle() on each bitmap, but I'm noticing that my memory doesn't seem to change.
Here's what the Memory Monitor shows:
As you can see, after loading the image I'm using about 60MB of memory. When I rotate the device that drops off a bit then comes back up. That leads me to think there is no leak, since the memory never goes above that.
When I click the GC button in the memory analyzer, my memory footprint drops dramatically to around 8 MB. This makes sense as the three bitmaps created during the process were recycled, so can be garbage collected. Then you can see that when I rotate again and the activity is rebuilt, the memory jumps right back up.
Here's my code to show you why so many bitmaps are created and when they're recycled.
void layoutImage() {
...
Bitmap bitmap = loadOrientedConstrainedBitmapWithBackouts(...);
imageTexture = new GLTexture(bitmap);
bitmap.recycle(); // recycle bitmap 2
}
Bitmap loadOrientedConstrainedBitmapWithBackouts(Context context, Uri uri, int maxSize) {
...
Bitmap bitmap = loadBitmapWithBackouts(context, uri, sampleSize); // create bitmap 1
...
Bitmap out = orientBitmap(bitmap, orientation); // create bitmap 2
bitmap.recycle(); // recycle bitmap 1
return out;
}
Bitmap orientBitmap(Bitmap source, int orientation) {
...
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight, matrix, true); // create bitmap 3
}
I'm not really sure that this is a problem, so to speak, since the memory isn't climbing (so no leak), but I'm curious when it stays so high. Since forcing a garbage collection clears it just fine, should I assume that if the system needs that memory it will be collected on the next GC pass? It's been running the whole time I've been writing this and is still sitting comfortably at 60 MB.
Question 1: Can I just trust that the garbage collector will take that memory back if needed?
Also, if we're supposed to be so judiciously recycling our bitmaps, why do so many of the Bitmap methods say things like "The new bitmap may be the same object as source, or a copy may have been made." Do I really have to check the equality every time I use those methods to recycle the bitmap if it's a different object?
Question 2: When using Bitmap creation methods, that may or may not return the same bitmap or a copy, do I need to check source and output equality to recycle the source if it's a copy?
Edit:
I have tried analyzing this with MAT, using a heap dump at peak usage (should be 60 MB), but it only reports 18.2 MB of usage and nothing unusual looking. Could they be reading things differently?
Question 1: Can I just trust that the garbage collector will take that memory back if needed?
Yes. If the incoming references are cleared, the garbage collector will take the memory when it is needed (typically for a new allocation). Calling recycle() doesn't help this process along or make it happen any faster.
The recycle() method exists because Bitmap objects were not counted against the heap until Android 3.0; so the method was helpful to assist the GC since it didn't otherwise have a record of that memory counted against its heap. In 3.0+, the memory is tracked against the heap so this extra bookkeeping isn't necessary anymore.
Question 2: When using Bitmap creation methods, that may or may not return the same bitmap or a copy, do I need to check source and output equality to recycle the source if it's a copy?
The createBitmap() method will return the same object if:
The source is immutable
x and y are both zero
width and height match the source width and height
No transformation matrices have been applied
Since it looks like you are passing in a transformation matrix, you will always get a copy unless the matrix is identity for some reason. But again, no real need to recycle() unless you are still supporting 2.x versions.

Avoiding java.lang.OutOfMemoryError

Hi all I am developing live wallpapers, and I am using lot of bitmaps. I have tested my new live wallpaper for aboute a week, and it was up and runing perfectly, but as soon as I have uploaded it to a market I keep getting this kind of exceptions : java.lang.OutOfMemoryError for both android.graphics.Bitmap.nativeCreate and android.graphics.BitmapFactory.nativeDecodeAsset. I use this kind of lifecycle of an bitmap:
I create a reference like:
Bitmap dark = null;
Bitmap cave = null;
at onCreateEngine I init them like :
cave = BitmapFactory.decodeResource(getResources(), R.drawable.cave);
dark = BitmapFactory.decodeResource(getResources(), R.drawable.dark);
here is where it trows the exception. for these images: . and after all I draw them to an canvas like this:
canvas.save();
canvas.drawBitmap(bg, new Matrix(), new Paint());
canvas.drawBitmap(dark, new Matrix(), new Paint());
canvas.restore();
What Should I do? It is better to load the dark image just one picture and draw it to the canvas width*height times? Or Are there any methods to do? I think recycle or calling onDestroy. But I do not know when to call them because the exceptions are thrown at onCreate.Are the images too big? And why it is working smoothly on my device, and on the other devices it is throwing exceptions? The bitmaps are 1484*1484 dimension big and the clouds are 250*172 dimensional big, should they be in 2^x * 2^x dimension?
Just try to use Memory Optimizer and see where are you creating Memory Leaks. You can use Eclipse Memory Analyzer(MAT) for this. Its a very common problem with using bitmaps. By using BitMaps you need to be extra careful for memory leaks.

Can't free memory of built drawing cache in Android 3.0

I have an application with a menu, where the menu items are screenshots from ViewAnimator's views. Everything is working fine. I do the screeshots with this simple sniplet, using drawing cache as written in many examples:
// Drawing cache is off, so build it manually and create scaled bitmap
layout.buildDrawingCache();
Bitmap bm = layout.getDrawingCache();
Bitmap bm_small = Bitmap.createScaledBitmap(bm, item_width, item_height, true);
In the same function I try to free all memory used for creating screenshot:
layout.destroyDrawingCache();
bm.recycle();
bm = null;
But unfortunatelly the garbage collector does not free this bitmap memory. I used also HPROF memory analyzing to find some references to Bitmap that cannot be freed but I did not succeeded. Important information is, that I am developing for Honeycomb Android 3.0, so the screenshots are quite big - every screenshot takes approx 3MB of memory and do not free it.
I don't understand, why recycle is not working in this example. I suspect, there is some very special problem in my setup: Android 3.0 Honeycomb + Hardware acceleration enabled + Large heap enabled + Using drawing cache. None of the hints I have found are not helping.
Please, can you explain, why recycle isn't working in this case? Any help will be very appreciated.
yes, I had this issue. it is very bad behavior because bitmap won't free itself. best advice is to use smaller bitmap tiles
and other advice is to use SoftReference<Bitmap> to store your data objects. SoftReferenced objects delete themselves when memory is needed. Careful though, you can wind up with missing objects.
the bitmap method though, is just flawed.

Bitmap size exceeds VM budget, not understanding why

I've looked all over for "Bitmap size exceeds VM budget" problems, but none of the solutions seem applicable for me. I am not understanding why my program sometimes throws this error because they way I'm using it doesn't seem to cause any possible memory leaks. My stack traces are pointing to the BitmapFactory.decodeResource() method. I've got a background image that I'm using to draw on a Canvas and this is how I've been initializing it:
Bitmap backgroundImage = BitmapFactory.decodeResource(getResources(),
R.drawable.background);
backgroundImage = resizeImage(backgroundImage, w, h);
This is how I've been using it:
canvas.drawBitmap(backgroundImage, 0, 0, paint);
I thought that putting backgroundImage = null in the onDestroy method would help, but that did nothing. There is not other reference to the background image resource in my program except in an XML file, but I don't think that affects it. Could someone explain to me why this is happening and how to fix it?
By the way, there is not screen orientation changes involved in this app.
You need to free the bitmap pixels when you're done with it. You mentioned that you set its value to null, but that only makes it eligible for GC, it does not explicitly tell the VM that you're done with those pixels, and that now is a good time to free them.
Before you set it to null, simply call Bitmap#recycle() on the Bitmap:
protected void onDestroy() {
if (this.backgroundImage != null) {
this.backgroundImage.recycle();
this.backgroundImage = null;
}
}
Additionally, you may be wasting resources in your resizeImage() method, which you did not provide code for. It's much more efficient to do proper down-sampling of the Bitmap at decode-time, rather than loading the full-size Bitmap, and then scaling it down from there.
The general technique is to use the 3-argument version of BitmapFactory.decodeResource(), with BitmapFactory.Options#inJustDecodeBounds for a first-time-pass in order to get the width/height of the Bitmap (although in your case, since it comes from the app's resources, there's no reason you should even have to do that.. but I'll explain it anyway); then determine a proper samplesize based on the target size, and decode the bitmap a second time. That usually results in much less memory usage, especially for very large images (e.g., with inSampleSize set to 2, it decodes the full-size Bitmap, but only allocates enough memory for a Bitmap of half the original size, downscaling the Bitmap in the process).

Categories

Resources