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
Related
Until now I'm using a SoftReference Cache for images in Android. This cache is used for images that are shown in ListViews and should help holding images of items in memory that are not shown on the screen, if there is enough memory left.
The problem with this is that SoftReferences are garbage collected nearly at the very moment the last hard reference is released. The result of this is that an image that is removed from the screen is garbage collected at that moment and if the user scrolls back to this entry in the ListView the image is reloaded from the internal phone memory resulting in a complex lazy loading process, resulting in frequent redraws of the list and general bad performance.
The bug request for the soft reference behavior states that this is the intended behavior and that you should use a LRU-Cache for caching this kind of stuff.
Know to my question. The LRU cache will only take as much memory as I allow him to. But if the app needs a lot of memory it will not free memory. How should I determine how much memory I can allow the Cache to use, and is there a way to reduce the size of the cache if the memory situation of the phone becomes tight?
At the moment the image cache is saved inside the application as a kind of global image storage for all activities. This would result in my app constantly using all the memory of the image cache even if my activities are in the background or destroyed.
Keeping background processes alive is an OS-level optimization that makes switching back to your process fast. Your process will stay alive only as long as the OS can afford the memory; when that memory is needed for another application your process will be killed and its resources will be released.
If you free your cache each time your process is backgrounded, switching back to your application would no longer be fast because it would see cache-misses. This defeats Android's keep-background-processes-alive optimization!
You should just use the LruCache, which is also included in the Android Support Package for releases prior to Android 3.0 (Honeycomb).
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.
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.