I am writing a very memory intensive application for Android Honeycomb, and I've been very careful to recycle()
unused Bitmap
s wherever possible; indeed, this is necessary for the application to work at all, as Bitmap
s 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
View
s to display a defaultDrawable
; - calls
recycle()
on theBitmap
s previously used by theseView
s; - nulls references to the
Bitmap
s.
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 Bitmap
s 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.
I have discovered that, in Honeycomb onwards, if an
ImageView
contains aBitmap
that has been recycled, theBitmap
data is still retained in memory untilsetImageBitmap(null)
is called on the ImageView. This may even be the case ifsetImageResource(...)
orsetImageDrawable(...)
are called (in this case, a very large bitmap was replaced with a fairly small nine-patch, but only whensetImageBitmap(null)
was called before loading the nine-patch was the memory actually disposed).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.
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.