When one should take care of Bitmap memory management or recycling bitmap in android ?
For example , there are few ways to create a bitmaps in android like following
Bitmap.createBitmap
Bitmap.createScaledBitmap
BitmapFactory
But when android allocate memory for bitmap which must be cleared so that in future application we won't face running out of memory error problem
In Android versions prior to 3.0, the Bitmaps are allocated outside of the VM. Android gets back this memory in Bitmap's finalize() method. You can let Android reclaim the memory faster by calling Bitmap.recycle() instead of waiting on the GC to call finalize() on them.
This is only really a problem if you're creating and discarding a lot of the Bitmaps. That is, if you are allocating memory faster then the GC can clean up the garbage left behind, at which point you get an OutOfMemoryError.
In android 3.0 and later, the Bitmap memory is allocated inside of the VM, so Bitmap memory can be reclaimed without having to call finalize() on them.
you recycle the bitmap whenever you don't need it. for example
#Override
protected void onPause(){
super.onPause();
if(bitmap !=null){
bitmap.recycle();
bitmap = null;
}
}
#Override
protected void onResume() {
super.onResume();
if(bitmap !=null){
bitmap.recycle();
bitmap = null;
}
}
P.S bitmaps memory are acting different from each devices, what i mean is
Growing Heap
is different from device to another
for example Bitmap size can exceed VM budget(Growing Heap) up to 240MB on the S4 tested and confirmed by personal testing and it doesn't cause OutOfMemoryError but in some other devices if the bitmap size exceed (Growing Heap) up to 16MB it may cause OutOfMemoryError its quite different from device to another because some devices has large heap while some not. and trust me dealing with Growing Heap is not easy task.
additional advice is to use android:largeHeap="true" in your application tag inside of the manifest.
`
Related
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.
I am trying to create cached image system for Android but the memory consumption just grows and grows. I looked through Android website for some ideas, but the issue just doesn't want to disappear.
Below is my code of getting the image from SD card, setting it and later destroying.
What am I doing wrong?
WeakReference<Bitmap> newImageRef;
public void setImageFromFile(File source){
if(source.exists()){
Bitmap newImage = BitmapFactory.decodeFile(source.getAbsolutePath());
newImageRef = new WeakReference<Bitmap>(newImage);
if(newImage != null){
this.setImageBitmap(newImage);
}
}
}
#Override
protected void onDetachedFromWindow() {
Bitmap newImage = newImageRef.get();
if (newImage != null) {
newImage.recycle();
newImage = null;
}
Drawable drawable = getDrawable();
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
Bitmap bitmap = bitmapDrawable.getBitmap();
if (bitmap != null){
bitmap.recycle();
}
}
this.setImageResource(0);
newImage = null;
newImageRef = null;
System.gc();
super.onDetachedFromWindow();
}
If you are using Android version >3.0 you dont have to call recycle()as the gc will clean up bitmaps on its own eventually as long as there are no references to it. So it is safe to remove recycle calls. They do nothing much here.
The code which you posted looks neat but are you sure there the leak is not happening somewhere else. Use Android Memory Analyzer tool to see where the leak is happening and then post the info.
Good luck.
Try to use Drawable.setCallback(null);. In Android 3.0 or newer, you don't even need to recycle because of more automatic memory management or garbage collection than in earlier versions. See also this. It has good information about bitmap memory management in Android.
As of this code it's hard to check if there is a detailed bug as this seems to cleary be a simplifyed version of the "full cache". At least the few lines you provided seem to look ok.
The main issue is the GC seems to be a little strange when handling Bitmaps. If you just remove the hard references, it will sometimes hang onto the Bitmaps for a little while longer, perhaps because of the way Bitmap objects are allocated. As said before recycling is not necessary on Android 3+. So if you are adding a big amount of Bitmaps, it might take some time until this memory is free again. Or the memory leak might be in anothe part of your code. For sophisticated problems like that its wise to check already proven solutions, before re-implementing one.
This brings me to the second issue: the use of weak refrences. This might not target the main problem, but is generally not a good pattern to use for image caches in Android 2.3+ as written by android doc:
Note: In the past, a popular memory cache implementation was a SoftReference or WeakReference bitmap cache, however this is not recommended. Starting from Android 2.3 (API Level 9) the garbage collector is more aggressive with collecting soft/weak references which makes them fairly ineffective. In addition, prior to Android 3.0 (API Level 11), the backing data of a bitmap was stored in native memory which is not released in a predictable manner, potentially causing an application to briefly exceed its memory limits and crash.
The way to go now is to use LRU Caches, which is explained in detail in the link provided about caching.
I read a lot of posts and comments on the Internet about Bitmap and Memory Leaks on screen rotation.
Actually, the issue i am experiencing is quite particular...
I use Eclipse DDMS and I am watching at the heap memory occupied by 1 byte arrays, which are related to my bitmaps placed in four ImageView, each one of them in a page of a ViewFlipper.
The images are loaded from internal memory and are 216-220KB big, and I use a layer drawable in order to draw another transparent image on top of them.
Keeping an eye on the heap memory occupied by the bitmaps, if I change the orientation of the device, the memory increases of some MB. Causing 2-3 GC, in a few moments the amount of memory decreases to the initial value.
Slowly repeating the process (change orientation + 2-3 Force Garbage Collection), the amount of memory increases but then goes back at the same value.
This makes me think I am doing it right with Bitmap management in my application.
But if I start rotating repeatedly and quickly the device, I see the amount of memory increasing continuously.... and subsequent GC will only be able to reduce the heap occupation by a little amount of memory in comparison to its rapid growth during fast rotation.
Is Android unable to Garbage collecting so fast?
Why am I able to keep memory usage stable if I slowly rotate and not if I repeatedly rotate
faster without forcing GC for a while?
Android 4.1.2 / Samsung Galaxy S3.
The code below is called whenever a png is read from internal storage or downloaded by
an AsyncTask from the internet.
Disabling AsyncTasks (thus excluding pending tasks through subsequent rotations) does not
change the scene, it is enough to load the two "layers" from the internal storage at
startup to quickly fill the heap while rotating the device.
public void setBitmap(Bitmap bitmap)
{
try{
Drawable layers[] = new Drawable[2];
Bitmap fvgBackgroundBmp = BitmapFactory.decodeResource(getResources(), R.drawable.fvg_background2);
layers[0] = new BitmapDrawable(getResources(), fvgBackgroundBmp);
layers[1] = new BitmapDrawable(getResources(), bitmap);
LayerDrawable layerDrawable = new LayerDrawable(layers);
setImageDrawable(layerDrawable);
mLayerDrawableAvailable = true;
}
catch(Exception outOfMemory)
{
Log.e("setBitmap got exception", outOfMemory.getLocalizedMessage());
}
}
The method unbindDrawables() called from the Activity's onDestroy() does not help:
public void unbindDrawables()
{
if(mLayerDrawableAvailable)
{
LayerDrawable lDrawable = (LayerDrawable) getDrawable();
if(lDrawable != null)
{
lDrawable.getDrawable(1).setCallback(null);
lDrawable.getDrawable(0).setCallback(null);
lDrawable.setCallback(null);
}
}
}
onDestroy is not guaranteed to be called when memory pressure is high. This might explain it. Try calling unbindDrawables from onStop()
http://developer.android.com/training/basics/activity-lifecycle/stopping.html#Stop
I would try to get back Bitmap from Your layers drawable and recycle it.
public void unbindDrawables()
{
if(mLayerDrawableAvailable)
{
LayerDrawable lDrawable = (LayerDrawable) getDrawable();
if(lDrawable != null)
{
((BitmapDrawable)lDrawable.getDrawable(1)).getBitmap().recycle();
((BitmapDrawable)lDrawable.getDrawable(0)).getBitmap().recycle();
lDrawable.setCallback(null);
}
}
}
Try to call unbindDrawables() in body of onDestroy() event in Your activity.
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
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.