Android: garbage collector not working for Bitmap - android

I have a ViewPager with a couple of fragments. In a fragment onCreateView,
I decode a couple of bitmaps that are assigned to fields in the Fragment class. If I try to swipe between the fragments, at a certain point the application gets an OutOfMemory exception (heap is exhausted).
Ok, this is a really bad practice, but, isn't the GC supposed to free memory before my application is killed?
Reassigning the Bitmap to the same field should cause the previous Bitmap to be released, am I wrong? On S4 I get the exception very soon.

Suppose you load a bitmap, lets say that is the first and then assign a new decoded second bitmap to the first, the first bitmap is not GC'ed when you decode the second one. GC will do it later whenever it decides. If you want to free memory ASAP you should call recycle() just before decoding the second bitmap. Src: here. And, go through Android's Managing Bitmap Memory article. Refer this too.

Fragments are kept in memory unless stated otherwise, so either you manually detach and dispose of fragments when swiping or mBitmap.recycle() when swiping.
Edit, code:
final FragmentTransaction fm = getActivity()
.getSupportFragmentManager().beginTransaction();
fm.replace(R.id.fragPlayerMain, playerFragment, "fragment").addToBackStack(null);
fm.hide(thisFrag);
fm.detach(thisFrag);
fm.commitAllowingStateLoss();

Related

How to release all memory allocated by an activity?

I have an activity that uses recyclerView and for each item's view there is an Image.
I have used Garbage Collector GC() on Destroy as follows
recyclerView= null;
adapter=null;
Runtime.getRuntime().gc();
But the following thing happens while releasing memory
And When I start another activity that loads images from a remote host using Picasso It says
java.lang.OutOfMemoryError: Failed to allocate a 94784012 byte allocation with 4194304 free bytes and 87MB until OOM
I found an answer that works for me to overcome that OutOfMemoryError
by Increasing heap size for application But I still want to release and ensure that memory occupied by an Activity is released instead of going to increase heap size for app.Thanks in advance , please help to do that task.
The code that you are using in your onDestroy method is not needed there. If destroy is called your acitivity will be removed from the stack and is free for gc anyway with all the resources in it that are only referenced by the activity.
OnDestroy doesn't always get called that's why your method may not be called at all. You could try and explicitly call for finish() in activity then onDestroy will be called and see how the situation will change.But then the activity will be removed from stack.
Also to call for gc manually is supposed to be bad style. On Android the system nearly always knows when it is the best time to do a garbage collection. Most of the times an activity finishes garbage collection is triggered automatically.
I would look into resizing images you get from Picasso first as they just could be too big in general for you heap even if there is space.
Look into resizing options link
Seems the problem is related to huge images, try to not keep images in memory, load images only on demand(with some cache strategy)
Try to replace picasso with glide. Glide provides better memory performance https://medium.com/#multidots/glide-vs-picasso-930eed42b81d
Try to load all your images with closest context
https://stackoverflow.com/a/32887693/6193843

Memory not freeing after fragment is removed

I have a Fragment which has a RecyclerView.
In this RecyclerView, I may occasionally download and display images (loaded with Glide into ImageView.
So when I open the Fragment, used memory may sometimes jump from around 30MB to around 100MB or even more.
After the Activity that is holding the Fragment is finished, the memory does not free up. It stays the same as before.
I checked Glide documentation and apparently we don't have to worry about freeing up Bitmaps in RecyclerView. This is a huge issue, because app often crashes due to OOM because of this.
How should I correctly handle freeing up memory when Fragment is removed?
Edit: another observation
Another thing I noticed is that if I finish the Activity and then start the same Activity again. Memory will jump back down for a moment and then back up to 100MB, which leads me to believe that the memory is cleared before launching the Fragment again.
Garbage Collection is sometimes a painful issue in Android.
Most developers fail to consider this issue and just keep developing without any sense of resource allocation.
This will of course cause memory problems such as leaks, OOM and unnecessary resource binding. There is absolutely no automatic way to free up memory. You can not, under any circumstances, rely solely on the Garbage Collector
Whenever you pass the Fragment's or Activity's onDestroy() method, what you can and should do is erase any construct that shall no longer be required in the application. You can do the following :
Avoid anonymous instances of listeners. Create listeners and destroy them when you no longer need them.
Set all the listeners (be them click, longclick, etc) to null
Clear all variables, arrays. Apply the same procedure to all the classes and subclasses contained inside the Activity/Fragment
Set the variable to null whenever you perform any of the previous steps on that given class (applies to all variables)
What I ended up doing was creating an interface like
public interface clearMemory(){
void clearMemory();
}
and implementing it on every class, be it Activity, Fragment or a normal class (includes adapters, custom views, etc).
I would then call the method whenever the class was to be destroyed (because the app was being destroyed or whenever I felt need to do so. Careful not to dispose in normal runtime)
#Override
public void onDestroy(){
clearMemory();
}
public void clearMemory(){
normalButtonOnClickListener = null;
normalButton.setOnClickListener(null);
normalButton = null;
myCustomClass.clearMemory(); // apply the interface to the class and clear it inside
myCustomClass = null;
simpleVariable = null;
...
}
By implementing this in a systematic way, my applications' memory management has become easier and leaner. One can then then know/control exactly how and when the memory is disposed.
This is adding on to Ricardo's answer.
You can add the following code to initiate garbage collection in Android:
Runtime.getRuntime().gc();
Note: Call this function after you've made all local variables null. Execution of this code doesn't guarantee that the system will garbage collect on your app, it merely hints that it might be a good time to do it.
I've used this in all my activities' onDestroy(), and it always seems to work when I want it to.
Give it a try, it might help you out.

Is it possible to recycle all data at OnStop() AND use a retainer Fragment?

1)It is considered a good tactic to recycle all bitmaps and data at activity's OnStop method.
2)It's also considered a good tactic to use a retainer Fragment to avoid recreating data at every configuration change.
But I don't see how these two can be combined?
Let's say I use a fragment to load a bunch of bitmaps...At OnCreate I check if that Fragment is null or not to get it's data or to instantiate a new one to create them. If i recycle all my bitmaps at OnStop() then there will be nothing left retrieve at the configuration change cause all data will have been recycled.
So....I don't see any way to combine these two tactics. Am I wrong? And if not which of the two is best to use?
My case is about loading images from SD card folder. could be only one pic, could be 500...
and showing pictues isn't all my app does so after this activity there could a need for memory by some other activity.
From Managing Bitmap Memory:
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.
According to this you don't even need to call recycle on devices running API 11 or higher, so it may not really be a problem for you.
You also really don't need to recycle bitmaps if the app is being destroyed as the system is going to reclaim all memory the app is taking up to begin with.
Recycle is only needed if you are showing a massive amount of bitmaps or large bitmaps and need the memory reclaimed in your app while it's still running.
Another thing to note is with the strategy you're trying, you wouldn't clean resources in the Activity's onStop() but rather the retained Fragment's onDestroy(). OnDestroy() on a retained fragment won't be called on configuration change because the Fragment isn't ever being destroyed. Thus, your resources can stay in memory beyond your Activity's lifecycle and will be destroyed at the end of your Application's lifecycle.

Bitmap.recycle() - "IllegalArgumentException: Cannot draw recycled bitmaps"

In the documentation, titled "Managing Bitmap Memory" you can find the following statement:
Caution: You should use recycle() only when you are sure that the bitmap is no longer being used. If you call recycle() and later attempt to draw the bitmap, you will get the error: "Canvas: trying to use a recycled bitmap".
So, what exactly means "no longer"?
I call setImageDrawable(drawable) in my fragments onCreateView(...) method. And I call recycle() on the drawable's bitmap in the fragment's onStop().
When the user now leaves the fragment by launching another activity the bitmap is recycled. But when the user comes back to the previous fragment, its onCreateView() is called again, resulting in a new call to "setImageDrawable(drawable)". And this throws:
IllegalArgumentException: Cannot draw recycled bitmaps
So, I still seem to be in the "no longer" context. When do I get a new bitmap, which is not recycled? Only after the fragment and its activity have been completely destroyed?
So, what exactly means "no longer"?
No longer means you are not going to use the same reference of Bitmap.
As you told you are recycling bitmaps on onstop(), try with this also inside your onStop()
if(bitmap!=null)
{
bitmap.recycle();
bitmap=null;
}
Bitmap and outOfMemory in android
Watch first 20 minuts of this official video if you want to make your day good - http://www.youtube.com/watch?v=_CruQY55HOk

When do we recycle an bitmap image variable

In my APP, Activity-A triggers another Activity-B. Activity-B triggers another Activity-C. Now the control return to the Activity-A from Activity-C Via Activity-B. The Activity-A uses the Bitmap image to draw as a background in its Canvas.
Question:
When I move from Activity-A to B, Do I need to recycle the bitmap variable (eg. background.recycle()) in onPause() method ?
Assume, I recycled the bitmap variable in onPause() method. Will this be a good approach to avoid OOM error ( keep in mind that the image has to be re-drawn when it comes back to Activity-A)
Do you get OOM errors? If not (since your bitmap is not big) then simply do nothing ... The docu says about the recycle method:
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.
So there is also a main difference between Android 2.x and Android 4.x. In Android 4 the bitmap memory is no longer managed natively, but is part of the normal java heap and works like every reference works: If the there is no reference anymore, then the garbage collector will collect the bitmap somewhere in the future.
Wherer is the right point to call recycle? Well the right point will be, when the Bitmap is no longer displayed on screen. so onPause could be a possibility, but keep in mind that you have to reload it (async) in onResume. So whats the problem with that approach? Once you have marked the Bitmap to be no longer needed by calling recycle() the bitmap is no longer useable, but probably has not been GC collected yet. So if the user jumps from Activty A to B and will return quickly, it's possible, that you have the same bitmap twice in memory, because the GC has not collected yet the first (recycled) bitmap.
So my tipp is: Try to reduce the bitmap size if you have memory issues. Use less quality bitmap (have a look at Bitmap Options). But I guess you need to try diffrent strategies to find the best working one for your app.
every time you don't need the bitmap recycle it. using
if method at least to avoid force closing in some cases.
if (null != bmp){
bmp.recycle();
}
and i advice you instead of sending the Bitmap between activities get its filename by using intent and pass it through your activities like this you will avoid re-drawing the bitmap in every activity and that will cause OOM for sure.

Categories

Resources