Memory Management

Every variable needs to be allocated some space in the computer's RAM and then de-allocated when it is no longer needed so that space can be reused for other variables. C# automates this process so you do not generally need to worry about it except to note that it has a performance cost which can be very significant in some cases. The Unity Manual has a more extensive page about Memory in Unity, but this section goes over the main points.

There are two general types of memory: the Stack and the Heap.

The Stack

When a Method declares a Value Type local variable, it will need to allocate a specific amount of memory for it. For example, an int takes 4 bytes while the size of a struct is determined by the types of its fields. Since that variable will only exist for the duration of the method, managing it is simple: you allocate memory for it when the method is called and de-allocate that memory when the method ends. If the method calls another method, the other method can allocate any of its local variables using the next available memory region. This type of memory is called the Stack because each new variable is allocated into the next available section of memory as if you had a stack of boxes. You can add a box to the top or you can remove the top box, but nothing can remove a box from the middle of the stack. It does not matter what size a new box is because it can always simply go on top of the previous one. This simplicity makes it very fast to execute.

The Heap

When you create an instance of a Reference Type, its lifetime is not so clearly defined. You might do nothing with it so it could be de-allocated when the method ends, but more than likely you will give something else a reference to it so it might last until an enemy is killed or the level is unloaded or even until the whole application is closed. This makes the memory management process far more complicated:

  • New objects cannot simply be allocated in the next available space. If you allocate a small object and a large one then de-allocate the small one, a new small object can be allocated in that same space but a new large object cannot.
  • It is no longer clear when exactly an object can be deallocated so it needs to check whether or not anything else is referencing that object. This process is called Garbage Collection and is the primary performance concern of this topic.

Unlike local variables, a Value Type used as a field in a Reference Type will be part of the memory allocated for that type and will therefore be on the heap.

Garbage Collection

The Garbage Collector is an automated runtime system that periodically checks through everything in the Heap. Any object that has nothing left referencing it can no longer be used by anything so it is considered to be garbage which can be collected (de-allocated so that the memory it occupied can be reused in the future). It also needs to account for the possibility that two objects might reference each other even though nothing else has a reference to either of them, in which case they are still garbage. This process is quite complex and can take a significant amount of time to execute, meaning that it can cause the frame in which it runs to take much longer than other frames, which users might notice as a stutter or lag in the game.

The main way you can avoid this performance issue is by avoiding the creation of garbage. Note that the performance cost is often insignificant and many people advise against trying to optimise things like this unless you actually find a problem occuring because optimisations generally involve adding more complexity to your program and therefore create more potential for bugs to occur.