This is part two of the bitmaps mini-series of posts.
Previously we discussed the basics of what a bitmap is, and how Xamarin Android handles them. In this post, I debated whether I should cover how to find bitmap memory leaks, or whether I should cover bitmap memory cleanup. Cleanup seemed to make the most sense, as you don’t need to be able to profile an app to know that you need to manage your bitmaps properly, so here we are.
We already discussed this topic, so I won’t go too deep into it, but I’ll briefly review it so it’s fresh. Xamarin Android has two runtimes, Mono and Android. Handles to native Android objects are created in the mono runtime, and objects in the native runtime have handles to kernel managed resources like bitmaps. Two garbage collectors run, the Mono and the Java garbage collectors. Objects that are shared between the runtimes have cyclic references to one another (object a references object b, object b references object a), and therefore both must have their references to one another broken.
That out of the way, let’s discuss how to make this happen. In C#, the way that you mark an object as being ready to be cleaned is via the dispose() method. All objects which implement the IDisposable interface have the ability to be disposed, and all objects in C# have access to the finalize method, you can read about their differences on your own time (or maybe I’ll do a post on that topic). In Java, objects are cleaned up via the JavaSystem.GC() method. Note that this doesn’t mean this is the only way to clean objects, this is just how you explicitly invoke a garbage collection cycle.
Drawbacks of the garbage collector
I’m not interested in the old garbage collector vs. manual memory management argument (I see the points on both sides), that said, the garbage collector does have some major performance hits when you explicitly invoke it.
To understand these drawbacks, we need to discuss the two types of collections: Minor and Major. To fully understand these concepts is for sure a post in and of itself (maybe even a series of posts), but suffice to say that there are two types of objects in the heap, short and long living (there’s actually more, but let’s call that good for now). Short living objects are those which get cleaned up rather quickly, and typically these include smaller pieces of data, such as strings, small arrays, and things of this nature. Long living objects are objects which will likely be used for some time before requiring cleanup (think views in Android).
This leads us back to minor vs. major collections. Minor collections are garbage collector cycles which cleanup short living objects. Minor collections will always happen if there isn’t enough memory to allocate a new object, when the nursery (a special section of the heap, which has different names depending on the garbage collector) is full, and in some other cases as well. It should be noted that when doing a collection, minor or major, all application threads are paused, this means several things. For starters, for each thread your application is using, the collection will take that much longer, because each thread stack must be observed and cleaned, and this can’t reliably be done if the threads are still working as it could modify the thread stack. It also means that your application will stop for a brief moment while the collection occurs. Minor collections almost (if not do) entirely ignore long living objects.
Major collections are where the entirety of the heap is observed and cleaned up, these tend to take significantly longer and doing too many of these types of collections will surely have negative effects on your applications runtime performance.
A word on garbage collector best practices
In the most ideal world, you never, ever force a collection, you mark items as disposed, null out the objects, whatever you have to do, and allow the garbage collector to naturally do a collection for you, this will give you the best performance. In some cases, this just isn’t an option, or it is an option but not the most ideal one (dealing in absolutes in programming is dumb). In the cases where a collection must occur, or where it’s better if it does occur (like cleaning bitmap memory in Xamarin Android), we need to know how to best do these collections. That’s what we’ll discuss next, I just wanted to say that you shouldn’t just abuse the garbage collector because it’s the safest way to keep your heap memory managed well.
Forced garbage collector invocation
In C#, the way you invoke an explicit garbage collection as mentioned earlier is with System.GC.Collect(), In Xamarin Android, to invoke the Java garbage collector, you use Java.Lang.JavaSystem.GC(). When attempting to clean objects shared between the two runtimes, you must invoke both of these garbage collectors if the memory needs freed right then and there. The act of invoking the garbage collector is referred to as a sweep. The reason I mention this is because I’m going to share some helper methods in the next post that I’ve created to sort of streamline garbage collection a little bit in Xamarin Android.
Bitmap recycling (and how it can bite you)
To properly dispose a bitmap in Xamarin Android, 4 things must occur, recycling of the bitmap, disposal of the mono handle, nulling out the mono handle, and a garbage collector sweep in both runtimes. Most of us probably know at this point what all of that means, except perhaps for recycling, so what is it? Recycling a bitmap means to mark it as being done with, you do this by calling the bitmaps .Recycle() method. Essentially you’re telling the operating system “I’ve finished using this bitmaps memory, please come and reclaim it as soon as possible.”. That’s all fine, but if you try to reference a bitmap which was cleaned up previously, you will get an error saying “Canvas: trying to use a recycled bitmap“, this is because the operating system has reclaimed this memory, you no longer have access to it, so trying to use it isn’t possible.
The reason the 4 steps must happen this way is because recycling the bitmap tells the Android runtime that the bitmap memory itself is ready for collection, disposing the mono handle marks the handle itself in mono as being ready for collection, and nulling out the mono handle breaks the cyclic reference to the Android runtime object, the sweep is optional as a sweep will occur at a later time, but if you need that memory to be reclaimed then and there, you should force invoke a collection.
That’s all for how to clean bitmaps, in the next post I’ll share some code with you that I use in all of my Xamarin Android projects which allows you to easily clean up bitmaps, image views that have bitmaps, and a technique I use to minimize the number of collections I have to force-invoke. Stay tuned for the next installment, and thanks for reading. Feel free to let me know anything I got wrong/need to fix and I’ll get to it as soon as possible.