I have big bitmap covering whole screen. After reading a lot of posts about memory leaks and performance I noticed 2 problems:
1. Generating big bmp costs time.
2. Caching big bmp /static bmp in activity/ speeds up activity start, but causes problem with references and memory.
My decision is to move bitmap in Application class.
How will this reflect on references and garbage collection?
Do I have to make bitmap in application class static?
UPDATE:
Please don't comment 'same memory' in both cases. My question is related to references and garbage collecting.
It's not important how you load bitmap when it is in memory.
It still takes the same amount of memory.
Related
I have a memory leak of bitmaps causing out of memory. I ran the tests on Android 5.0 (Samsung S5). I have investigated the issue using Android Studio (1.5.1 + 2.0.0 Preview 7). The HPROF memory dump show that there are multiple byte[] which correspond exactly to a particular huge bitmap I use temporarily. If I make sure I keep references to the bitmap then Android Studio shows me a Bitmap with 11MB dominating size and a byte[] with 11MB Shallow size. If I don't keep references to the bitmaps then some of the bitmaps become garbage collected and some end up as byte[] without incoming references (ie. no parents) as shown in image.
I have tested my app enough to know with reasonable confidence that this 11MB byte[] is a roughly 2891x1000x4 bitmap I have had in memory. Some smaller bitmaps also get leaked and show up with no incoming references.
The bitmaps above are allocated in a subactivity. If I return to the parent activity (in same process and thus dalvikVM) and force 2x GC then memory is released. Multiple manual GC's does not release the memory before quitting the sub-activity.
It seems to be independent on whether I run bitmap.recycle() or not. It happens very rarely if I just stand in the same place in the app and run the code generating the huge bitmap out of the same view. If I move around the app and generate the bitmap from different views its a lot more frequent, like from 50% leaks to 10% leaks.
Is it an Android bug?
Do I use the bitmaps wrong in some way and Android studio just fails to show me a correct view of the memory?
Is my understanding correct that if there are no parents in the reference tree below (see image), then the memory should be released by garbage collector (hence its an Android Bug or studio bug or hprof dump-bug?)
I found a solution to the leak, though Android Studios reporting of the unreferenced byte[] is still a mystery.
It appears that the ImageView in which i did
imageView.setImageBitmap(bitmap)
will prevent GC of the bitmaps underlying byte[]. bitmap.recycle() does not help, nor does unbinddrawables() as written about in numerous locations
if (imageView.getBackground() != null) {
imageView.getBackground().setCallback(null);
}
setImageBackground(imageView, null);
imageView.setImageBitmap(null);
imageView.setImageDrawable(null);
When I removed the view from the viewhiearchy and removed all my own references to the view, then the byte[] was GC'ed and the leak was gone.
To draw my sprites in OpenGL i use a single backing bitmap across multiple objects, the Bitmap is reused if size is big enough or recreated if too small. Currently i am using a static Bitmap object but i think this is causing memory leaks even if i am not sure about that.
So, let's say i need a single Bitmap shared between multiple objects, what would be the best approach?
1) Use a single Bitmap as a static reference as i do
2) Use a static weakreference (even if its not suggested in the android dev page here http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html)
3) Use a singleton and then a Bitmap inside it (but this would be like 1)
4) Use an LRU cache and just creating a new bitmap every time i need it to be bigger
1,3 & 4 are essentially all the same. You create a static reference to either your Bitmap directly or to something that holds a reference. The same happens when you use the Application class to "anchor" that bitmap. That class is kept by Android alive and is in this context the same as a static reference.
Whether this is a memory leak or not depends on your definition. Leaked objects are those that are kept safe from the garbage collector by unintentional references to them. So it's certainly not a leak while you want that reference to keep your bitmap.
The problem that arises with cached data that is independent of the life of some Activity, Fragment or in more general terms "task" is that the data will keep memory occupied even if the user is never coming back to your app. The app process is kept alive until Android decides it needs the memory. That time between your last legit use of the bitmap and Android finally killing your app and thereby cleaning the memory can be seen as leak.
If we had magic powers, we could simply clean up the cache once we know that is going to happen. There are some realistic options though:
Using Android's callbacks: understanding onTrimMemory( int level )
time limits on references: e.g. https://github.com/jhalterman/expiringmap
2) is not an option. If you're trying to use WeakReference as cache, you haven't understood what that class is intended for and I honestly don't understand why it is even mentioned in the documentation (weakly referenced objects should be garbage collected as fast as possible once nobody has a strong reference anymore).
SoftReference is intended for "caching" but using it as actual cache is not only broken on Android. It's broken by design because you give the garbage collector the responsibility to maintain a cache for you without telling it how to prioritize objects or how much memory it should keep guaranteed under what conditions. The result is that the GC will clean up the wrong thing or simply everything. SoftReference can be used to in addition to a proper cache that knows how to clean up.
In addition to all of that: be aware that a single Bitmap may not be enough. If you had a look at Tasks and Back Stack you may have noticed that 1 app process can have 2 or more independent tasks in parallel. That means there could be whatever Activity uses the bitmap in different stages. If you don't want to overwrite your cache bitmap between those all the time, you may have to have 1 bitmap per task.
I don't know how to do it per task, but you can easily use a retained fragment to tie the life of your bitmap to that of an activity (ignoring screen rotation etc): http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html / example with bitmap cache https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/util/BitmapCache.java
I think best way is to create Singleton with hard reference plus some methods for recycling and loading, I've created a lot different way Bitmap and graphics loaders and this is probably most efficient and easiest for access way
public class BitmapLoader{
public static BitmapLoader bl;
private static Bitmap b=null;
public static BitmapLoader getInstance(){
if(bl==null)
bl = new BitmapLoader();
return bl;
}
public Bitmap getBitmap(Context c){
if(b==null && c!=null)
b=loadBitmapUsingContectIfNeededOrWhateverYouWant(c);
return b;
}
public void recycleBitmap(){ //for e.g. onDestroy() Activity or other
if(b!=null)
b.recycle();
}
}
keeping loaded Bitmap in LruCache is also good idea
My application needs creating a bitmap object for a certain view every 1 min
private static Bitmap mBitmap = null;
public static Bitmap getBitmap()
{
//create new bitmap object
return mBitmap;
}
My question is, do I need to destroy mBitmap before creating new bitmap ?
You don't have to manually destroy a Bitmap after you used it, but you can help the garbage collector do its job. There is a method called recycle(), the following paragraph is from its documentation:
Free the native object associated with this bitmap, and clear the
reference to the pixel data. This will not free the pixel data
synchronously; it simply allows it to be garbage collected if there
are no other references. The bitmap is marked as "dead", meaning it
will throw an exception if getPixels() or setPixels() is called, and
will draw nothing. This operation cannot be reversed, so it should
only be called if you are sure there are no further uses for the
bitmap. 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.
In earlier Android versions Bitmaps were handled natively by the OS. This was the original reason for the recycle() method. Since the Bitmaps were handled outside of the Java VM the garbage collector could not automatically free the memory of unused Bitmaps, you can find more information about that here but the important part is this:
On Android 2.3.3 (API level 10) and lower, the backing pixel data for
a bitmap is stored in native memory. It is separate from the bitmap
itself, which is stored in the Dalvik heap. The pixel data in native
memory is not released in a predictable manner, potentially causing an
application to briefly exceed its memory limits and crash. As of
Android 3.0 (API level 11), the pixel data is stored on the Dalvik
heap along with the associated bitmap.
So if you want to support Android 2.3.3 (Gingerbread) or below you have to be careful with Bitmaps. You always have to remember to call recycle() otherwise your application may crash in an unpredictable manner.
If you only support Android versions above Android 3.0 then you don't have to worry about freeing Bitmap memory, but if you create a lot of Bitmaps and/or are getting close to OutOfMemoryExceptions then calling recycle() on all not needed Bitmaps can still have
a considerable positive effect.
If you want to learn more about handling Bitmaps then visit this link.
I hope I could help you and if you have any further questions please feel free to ask.
You should never do that, you should either:
Reuse the Bitmap if size is always the same (clearing and reusing)
Caching bitmaps using an LRU cache
If you absolutely need to destroy it every time call Bitmap.recycle() and then create a new one, but this is a very very bad thing to do.
On Android pre-honeycomb, Bitmaps have freaky memory issues because their data isn't stored in the VM. As a result it isn't tracked or removed by the GC. Instead it is removed when Bitmap.recycle() is called (and that is also done automatically in the Bitmap's finalizer).
This leads to some problems when doing image caching. When a bitmap is due to be evicted, I can't simply call recycle() on it, because I have no idea if anyone else is using it.
My first thought was to do System.gc() just before I load each bitmap. That way, hopefully orphaned Bitmaps will be finalized and the native memory freed. But it doesn't work. Edit: Actually it does sort of work. I had my System.gc() in the wrong place, after moving it, and halving my cache size (to what seems like a ridiculously small 2 MB of uncompressed bitmap data), my app no longer seems to crash (so far)!
My next thought was to implement manual reference counting, by subclassing Bitmap and calling ReferenceCountedBitmap.decrementCount() in all my activities' onDestroy() methods. But I can't because Bitmap is final.
I am now planning a BitmapManager which keeps WeakReference's to the bitmaps, and has methods like:
public void using(Bitmap bm);
public void free(Bitmap bm);
which count the references.
Does anyone have any experience or advice handling this? Before you suggest it, I can't ignore 80% of the market.
Well, I solved this with a bitmap manager, where I save the referencing views. In a map-like structure bitmap -> list of views.
Before I call recycle() on a bitmap, I first set all the references from the views to null (otherwise it will throw bitmap recycled exception).
Manual garbage collection, as you say, doesn't work for bitmaps pre-honeycomb, since they are allocated in the native heap and even with System.gc() you can't make assumptions when this memory will be released.
I am metting an OutOfMemory exception after a while, while inflating an XML View with a bitmap.
The problem occurs after opening many times activities that are using many bitmap but always inflated from the XML.
I check with MAT the Memory.
First question : I don't retain any of my Activity in memory
- If I filter on the name of my Activity, i get Objects = 0, but on some activities
I could see RetainedHeap >= 302. Does this indicate a leak ?
After that, I try to compare my memory in different point of time, to see the objects that have been created. But I can't find any thing significant.
My Question is : If I don't retain activities in memory what could be the leak reason ?
What tools/Method to use to find it ?
Regards,
Olivier
Try setting all references to the bitmaps to null when the Activity is destroyed. This may not answer your questions but it should solve your problem.
If you have big source images then scale them to target size: Strange out of memory issue while loading an image to a Bitmap object
Cache Bitmaps (but not Drawables!!) in a List (or Map) and save them in onRetainNonConfigurationInstance(): http://developer.android.com/resources/articles/faster-screen-orientation-change.html