AFAIK on Android, it is recommended to reference Bitmap objects as WeakReferences in order to avoid memory leaks. When no more hard references are kept of a bitmap object, the garbage collector will automatically collect it.
Now, if I understand correctly, the method Bitmap.recycle() must always be called to free a Bitmap. I think this is because Bitmap objects have special memory management.
Is that correct?
If this is true, when using WeakReferences, there must be memory leaks because Bitmap.recycle() is never called when the WeakReferences are freed. Or, somehow, are WeakReferences sufficient to avoid memory leaks?
Thanks
Bitmap.recycle isn't required to be called, as the garbage collector will clean up bitmaps on its own eventually (as long as there are no references). Bitmaps in Android are created in native memory, not on the VM heap, so the actual bitmap object on the VM heap is very small as it doesn't contain any actual bitmap data. (EDIT: no longer the case as of Android 3.0+) The real size of the bitmap will still be counted against your heap usage for purposes of GC and making sure your app doesn't use too much memory.
However, the GC seems to be a little moody when it comes to Bitmaps. If you just remove all hard references, it would sometimes (in my case) hang onto the Bitmaps for a little while longer, perhaps because of the weird way Bitmap objects are allocated/counted. Bitmap.recycle seems to be good for getting the GC to collect that object more quickly.
Either way, you won't leak memory if you don't call Bitmap.recycle as long as you don't keep hard references accidentally. You may encounter OutOfMemoryErrors if you try to allocate too many bitmaps at once or too large bitmaps without calling recycle, though.
EDIT: It is important to note that as of Android 3.0, Bitmaps are no longer allocated in native memory. The are allocated on the VM heap like any other Java object. However, what I said about not needing to call recycle still applies.
Related
I had a few questions about Picasso.
1. I saw here that the memory cache used by Picasso is just a LinkedHashMap which holds a strong reference to the Bitmaps. Shouldn't it have a WeakReference or a SoftReference to the Bitmaps? If not, then how will the images get cleared from cache when memory becomes low and GC runs?
2.In a heap dump collected from an app after closing the app on back-press and running the GC, i can still see the instances of Picasso.LruCache (image attached below) with Bitmaps in it. Why is this LruCache instance retained? Because its occupying a lot of memory.
3.Also Also i can see a lot of instances of PicassoDrawable class. That too after GC run
What is the difference between the heap usage (Allocated) we can see in the Elipse Memory Analysis Tool (in the DDMS view) and the memory usage size for the same App shown here on the Android device?:
Settings->Apps->Running
Even though I aggressively tried to preserve memory by making objects null as soon as they weren't needed, the latter number (memory usage size on Running apps screen) only kept increasing and my app finally crashed due to OutOfMemoryError. However, the former showed me that I was well within a reasonable size. I was also calling System.gc() a lot. Is there a difference between the two? Why the discrepancy? Any ideas on how I can solve this problem?
The biggest difference between the two that I know of is the scope of garbage collection.
Normal garbage collection, including System.gc(), collects a bit of garbage, then stops. It is not a complete sweep of the heap to get rid of everything. That is to try to minimize the CPU impact of garbage collection.
The heap dump prepared for MAT, though, effectively a complete GC.
Your symptoms suggest that you are allocating memory faster than GC can reclaim it. The primary solution for this is to try to allocate less memory, or allocate it less frequently. For example, where possible, reuse objects, bitmap buffers, and the like, instead of trying to let GC clean the old stuff and allocating new stuff as you go.
It sounds like you have a memory leak somewhere in your application if the memory is never released. This means that somewhere you are maintaining a strong reference to a large object which is being recreated (like an Activity or Bitmap) which is why calling System.gc() is making no difference.
I suggest watching the following on memory management in android from google IO 2011. It lets you know how to use the eclipse memory analyser tool which is incredibly useful for debugging this sort of error
I see my heap growing and I know it will eventually crash on any device since it just keeps growing.
Grow heap (frag case)
is seen throughout the logs
on my phone it will crash the app at 32mb used. Other phones will of course be 16mb, if there are any with that few resources that run android 2.2
Now, I am using recycle() on my bitmaps, setting things to null, popping items from complex data structures, and using System.gc() to invoke garbage collection, throughout the app
but, the heap still grows and its a problem... eventually
How do I just force the app to dump resources so that it can continue functioning.
it is usually always the "bitmap vm budget" exceeding, but I am feeling more and more that I just don't have access to the "clear bitmap vm" command
i had also struggled a lot with this issue. there are many a things you can do.
you can call recycle on each bitmap and set them to null.(bitmap.recycle() with relaese all the memory used by this bitmap but does not nullify the bitmap object ).
you can also unbind the drawables associated with layouts as specified in this link.
you can convert your hashmaps to WeakHashmaps. so that its memory would get relaesed when system runs low on memory.
you can resize all your bitmaps. have a look at this link..
you can override onLowMemory() method in activity which gets a call when entire system runs low on memory. you can release few resources there.
you can make your objects SoftReference or Weakreference, so that they get released in low memory condition.
But the real fact is that, all this can DELAY your out of memory issue/crash, but can not eliminate it because thing is that you must be leaking your activity context or memory somewhere.
I am writing a very memory intensive application for Android Honeycomb, and I've been very careful to recycle() unused Bitmaps wherever possible; indeed, this is necessary for the application to work at all, as Bitmaps are constantly being cycled in and out of memory. However, I have just implemented onConfigurationChanged() in the Activity, and so (for a number of reasons) I am trying to put memory freeing routines in onStop().
Currently my onStop() method:
sets some Views to display a default Drawable;
calls recycle() on the Bitmaps previously used by these Views;
nulls references to the Bitmaps.
Unfortunately, using the Eclipse memory profiler, it seems this is having no effect on the memory usage at all.
As you can imagine, having made so much effort to free resources in a nominally garbage-collected language, I would have hoped for a little more effect. So my question is: what does recycle() do? Does it actually trigger garbage collection, or will the system hold on to the memory—even if you call System.gc()—until it feels the need to get rid of something?
NB I know Bitmaps aren't actually held in the regular heap but I thought calling recycle() was enough to ensure they were dropped out of the native heap.
PART OF THE ANSWER
I have discovered that if an ImageView contains a Bitmap that has been recycled, the Bitmap data is still retained in memory until setImageBitmap(null) is called on the ImageView. This may even be the case if setImageResource(...) or setImageDrawable(...) are called (they were, loading in a relatively small nine-patch—however, MAT analysis shows this did not remove the large Bitmap, which was contained in the private members of the ImageView). Simply calling this function at onStop() has culled about 10MB from the heap of our application. Apparently this may not be the case for pre-Honeycomb builds of Android, though.
As Justin says, Bitmap data is not allocated in the VM heap. There is a reference to it in the VM heap (which is small), but the actual data is allocated in the Native heap by the underlying Skia graphics library. [Note that this may have changed in later Android levels, but is true for 2.1 and 2.2]
When you do a recycle() that marks both the small portion in the VM heap and the actual data in the native heap as free and available for GC. But the actual collection is performed by two different GC mechanisms. The portion in the VM heap is collected by the Davlik GC - and you can see that happening via DDMS. But the native heap data is collected by the Skia GC, which appears to be lazier (it runs less frequently?). That means that, even with rigorous recycle()s, it is possible to get ahead of the native heap GC.
Fortunately there are mechanisms to monitor the state of the native heap. See BitmapFactory OOM driving me nuts.
I have discovered that, in Honeycomb onwards, if an ImageView contains a Bitmap that has been recycled, the Bitmap data is still retained in memory until setImageBitmap(null) is called on the ImageView. This may even be the case if setImageResource(...) or setImageDrawable(...) are called (in this case, a very large bitmap was replaced with a fairly small nine-patch, but only when setImageBitmap(null) was called before loading the nine-patch was the memory actually disposed).
Recycle frees the native memory that is allocated to the bitmap. The actual Bitmap object will remain in the Dalvik Heap until the next garbage collection (but the memory taken up by this object is insignificant).
As far as I am aware, there really is no way to dump the native heap. So you won't be able to see if the bitmap's native data is gone via a heap dump. You should see, however, the total amount of memory your application is using go down. This question should help you discover the various ways to access the memory usage stats of your app.
I'm currently using a filesystem cache to cache my images as I download them from the server. I have a ListView that contains custom views, each of which retrieves its image from the filesystem when getView() is called.
To improve performance, I implemented a java.util.WeakHashMap<String,Bitmap> that stores each of the bitmaps by a unique key. This allows me to tuck the images into the hashmap as they're downloaded, and then retrieve them directly from memory to populate my listview. This avoids a file I/O operation and results in a much smoother scrolling experience.
The idea is that as the OS runs low on memory, it will clean out the WeakHashMap to free up memory.
However, this doesn't work on Android 2.3 or earlier. The problem is that bitmaps are not kept in the Java Heap, and are instead kept in native memory. This means that the JVM garbage collector has no idea how much memory those images are occupying, and thus never bothers to free them up when the OS is low on native memory, resulting in OutOfMemory errors when there's plenty of memory that can still be reclaimed.
This has been fixed in Android 3.0, since 3.0 stores bitmaps in JVM heap instead of native memory, but the question is how can I easily cache bitmaps on Android 2.3 and later without inadvertently causing unnecessary OutOfMemory exceptions?
You might try something like this: http://code.google.com/p/xlarge-demos/source/browse/trunk/PhotoAlbum/src/com/example/android/photoalbum/LruCache.java and explicitly recycle() your Bitmaps in the evicted step.
So the answer seems to be that there's a bug in the way the dalvik VM detects when it needs to do a GC pass. If you manually call System.gc() immediately before allocating memory for your bitmap, the OutOfMemory errors surprisingly go away.
if(Build.VERSION.SDK_INT < 12) {
Log.d("Running garbage collection for Bitmaps");
System.gc();
}
return BitmapFactory.decodeStream(is);
Obviously, the VM should be doing this GC automatically before it throws an OutOfMemory, but it does not appear to do so.
So, even though Bitmap memory is allocated on the native heap in earlier versions of Android, this memory is still charged against your process, its just harder to see, this is why you might get an OOM Exception. However, your basic analysis is correct though. The problem is that the native code doesn't really have a good idea when it can deallocate memory for Bitmaps, which is why its recommended that developers all Bitmap.recycle(), since this essentially tells native code that its okay to free the memory. Likely when items are removed from the WeakHashMap, this isn't being called.
However, empirically I'd built a similar system using a HashMap<String, SoftReference<Bitmap>> and Bitmap memory was properly freed. I'll note though that I think this solution became less effective starting in Android 2.3 because of changes to the garbage collector, although I'd need to go back and verify this remembrance.
In the end I guess the answer is that I don't know of a good answer to this question that doesn't use explicit management like the LruCache. It would be great to have a solution that uses SoftReferences or WeakReferences, but with the current way we do garbage collection I'm not sure this will work.