In this post I’d like to take a look at the Mono SGen garbage collector. SGen is the garbage collector of choice for Xamarin, and as such this makes it important to understand it’s inner workings if you care about performance or understanding how your Xamarin apps work at a deeper level. Let’s jump right in.
Garbage collectors are simply the portion of a language or runtime that make sure your program is cleaning up it’s heap memory before it causes the application or system to run out of memory. To do this, many different types of algorithms are implemented from garbage collector to garbage collector. Some garbage collectors require all threads stacks to freeze when cleaning memory, some can work concurrently, but the point is that garbage collectors clean up memory.
SGen’s memory management model
The SGen garbage collector works by doing a series of major and minor collections. To do this, SGen divides up the heap into 4 segments:
- The nursery
- The large object store
- Old generations
- Pinned chunks
The garbage collector will always be triggered whenever the operating system asks for memory and there is none to give, to keep the application from crashing. The garbage collector does sweeps on major and minor collections as mentioned previously, but more specifically, SGen operates on major and nursery collections. This two pronged approach allows the garbage collector to more efficiently clean up smaller, short lived objects. When the SGen garbage collector goes through and does a sweep, all objects present in the nursery are checked and cleaned if needed. If any objects survive the nursery collection, those objects get moved to the old generation portion of memory, and whenever both the nursery and the old generation segment are full, a major collection gets run. For the SGen garbage collector, all threads must be stopped and analyzed to prevent threads from modifying the state of the heap.
When the application starts up, a block of memory that is 4mb in size by default is created, this block of memory represents the nursery. You can change the amount of memory allocated to the nursery via the environment variable MONO_GC_PARAMS when invoking Mono, but keep in mind this means potentially less collections, but longer collection times, so make sure to understand that you want to strike a balance here.
Large object store
Larger objects are expensive to move around, when memory becomes fragmented in the heap, it needs to be shifted around for other objects to be allocated, and if you need to do this with larger objects, this would get extremely slow, so SGen will actually allocate large objects via operating system pages. This allows the garbage collector to simply cut ties with the memory and let the operating system deal with it’s cleanup.
Pinned objects (chunks) are pretty simple to understand, even though the documentation for the SGen garbage collector makes them kind of hard to understand. A pinned object is simply an object that needs to be kept at a definite memory location because some other thing, usually unmanaged code, relies on it being there, this causes memory fragmentation because the object can’t be copied over somewhere else causing that continuous block of memory to be fragmented. From what I understand, pinned objects will stay in place until they are able to be unpinned, and this is a shortcoming of the SGen garbage collector, if I have this wrong, feel free to reach out and correct me.
How cleanup works
We’ve covered the various portions of memory managed by SGen, and what they are. Now, let’s cover how the garbage collector actually does it’s job. SGen uses graph coloring as it’s cleanup method, you can literally think of this as a color coded picture. A graph of all objects in the particular portion of the heap being observed gets created, and the graph is generated as a tree. Roots are generated as the top level of the tree, these are typically going to be either base objects in a chain of references, or things like static objects that will never be collected. In SGen, all leaves of the tree get colored white, signifying nothing has been done with these leaves yet. Next, all garbage collector roots are marked gray, and identified as root objects; gray means an object can be reached, but it hasn’t been scanned for further references. From there, the objects get scanned for further references, and when the garbage collector finds more references, it colors them gray, and colors the object that just got scanned black, meaning it’s been reached and has been fully processed.I should say that by reference, I mean an object relies on another object existing in some way or another, for example a view in Android is typically in a chain of references to an activity, an activity to a context, etc.
When does cleanup happen then? Well, any left over objects which were left white are considered unreachable. This means that no other objects reference them, and they are clear to be collected. The nursery works in the exact same way, except when an object gets colored, it means it needs to also be copied to the major heap as it isn’t a short living object.
Frankly there’s a lot more to the SGen garbage collector that to be honest, I just don’t know enough about to sit here and talk about, so for anything I didn’t cover, I’ll point you to the SGen documentation where the pros cover it in detail. My goal with this was to simplify the basics that a programmer will want to concern theirselves with on a day to day basis, the other stuff is cool to know and can be useful I’m sure, but it’s just not vital to know for most things. Hopefully you found this helpful, and if you have any questions or corrections, please feel free to reach out to me. Until next time.