Michał Muzyczka
Michał Muzyczka's Blog

Michał Muzyczka's Blog

Garbage Collector in 5 minutes

Garbage Collector in 5 minutes

Michał Muzyczka's photo
Michał Muzyczka
·Nov 27, 2021·

4 min read

Subscribe to my newsletter and never miss my upcoming articles

We are constantly allocating memory. We instantiate ints, objects of our classes, load files. The big advantage of writing in C# that we don't see every day is the garbage collector.

Someone might ask "if I didn't know how it works so far, why should I now?". In my opinion, there are four reasons why you should know.

You are writing in Unity You are writing unmanaged code (but if you are, you probably learned how the garbage collector works a long time ago) You want to have a better big picture of what you do every day You are preparing for a job interview

No matter which point applies to you today, feel free to read on!

Managed heap

By default, when we write every day, we write in a managed environment. Until we use the keyword 'unsafe'. This means that we don't have to worry about memory management - all allocated objects are stored in the managed heap. Once in a while, the garbage collector checks this list. Of course, there are many objects on this list, so to avoid searching the whole list, GC "divides it into several parts":

Generation 0 - these are mostly temporary objects. Often only just initialized. Most of them do not go to the next generation.

Generation 1 - if something was not removed from generation 0, then it goes to the first generation. GC removes from generation 0 what is unreachable (it went out of scope, there are no other objects that hold the references). It serves as a buffer between short- and long-lived objects.

Generation 2 - long-lived objects. These are objects that exist for most of the application life. There is also a large object heap (which is sometimes called generation 3), but it is collected together with the 2nd generation. Each successive generation makes the object to be checked less often.

IDisposable

Some classes implement the IDisposable interface. As the name says, these are objects that can be "disposed". And if they can, they should too. For example, most classes with Stream in their name implement this interface. Often, we use using with them.

using (FileStream fs = File.OpenRead(path))
{
    ...
}

This is not a coincidence, because to use using in this context, the object's type needs to implement IDisposable. This pattern means that Dispose will be called at the end of the block. What if I don't call Dispose nor I don't use using? Nothing. The GC at cleanup will call Finalize itself, which in turn will call Dispose. But it is a good practice to make sure to call Dispose yourself. Because you don't always know when exactly GC will be called (unless you call GC.Collect() yourself)

Garbage Collector in Unity

Unity's memory management could be a separate, huge post. That's why I'll limit myself to three points that I think are the most important.

  1. The garbage collector will be called in the next frame. It's important because if we have limited memory (e.g. 512MB) we can get Out of memory exception. Even if we call DestroyImmediate. On top of that, our assets can be large, so their allocation and deallocation can cause slight FPS drops.

  2. Memory is like blocks. If our object is big, it needs a lot of blocks next to each other. Sometimes these blocks can be alternately: free, allocated, free, allocated... and even though we have memory, we may have a fragmentation problem. That is, we will not be able to allocate memory for a large object. However, usually, we don't have to worry about this, it "fixes itself".

  3. The memory occupied by Unity is smaller than the memory used by our application. Once the memory is used by Unity, it is not returned to the system. If our application is using 512MB 80% of the time, and we have a peak at 1024MB 20% of the time, then our user will see that our application is using 1024MB 100% of the time

As we can see most of the time we don't have to worry about memory. Thanks to CLR everything is doing itself, and programmer intervention is required only in extreme cases. However, we must remember that such extreme cases in programming are many. And the more of them we know, the better we are.

You can read more about Garbage Collector here docs.microsoft.com/en-us/dotnet/standard/ga.. And about memory in Unity here docs.unity3d.com/Manual/performance-managed..

 
Share this