Wednesday, June 27, 2012

Garbage Collection


When programming games or any other kind of real time application in a managed environment (Ala .NET) one must pay extreme attention to the garbage collector if you care about this thing called performance, or, constant frame rate. I've seen plenty of blogs on the internet talk about this topic and give some examples of when garbage occur, but I thought I would gather some thoughts and experiences on the subject that I have managed to collect myself (see what I did there?).

I will also be talking from a Unity 3D perspective as it is the engine I use, which means it's not really MS .NET I'm talking about, it's really the Mono implementation. Actually, it's not even the official release of mono, Unity keeps their own version of mono to assure compatibility with all their beloved code, but it should apply to other implementations as well. Just don't take my word for it.

Before we jump in, the basic knowledge you must have is the difference between value types and reference types.

Value types get's put on the stack which is like a fast access buffer you could say.
These include:
* Any Number type (byte, short, int, long, float, double etc.).
* The DateTime struct.
* Or any other struct type

Reference types on the other hand will be put on the heap when created and when no longer used by the program, they will need to be collected and destroyed from memory, hence the term garbage collection. The most obvious cases include:
* Any class type
* Arrays (It doesn't matter what is stored in the array, the array itself is a reference type)
* Strings

If you want to know all the ins and outs of stack vs heap, value types vs reference types: I suggest you google the subject and get a good explanation from someone with the proper knowledge. MSDN is a good place to look.
Also, just to clarify; this example shows when a garbage collection will occur, it will not happen until you specifically are not using a certain object by nulling it's pointer, or in other terms, not having a variable in your program that holds a reference to the object in memory:

object obj = new object();

// do some stuff with our brand spankin' new object!

obj = null;    // we no longer need this object, let the garbage collector deal with cleaning up after us.


Example using unity objects:

GameObject obj = new GameObject();

// do stuff

Destroy(obj);


The garbage collecting process won't happen instantly, it is instead run on interval's usually in seconds in between or so.


So sometimes it remains unclear what other things generates garbage and I thought I might shed light on as many things as possible, so here follows a list of things that will feed the garbage collector that you need to be careful about:


* Casting a value type to reference type. This is called boxing. Example:

int number = 5;
object obj = (object)number;    // This will generate garbage


* Using a struct by passing it as a reference type. This is also called boxing. Example:

bool CompareTwoThings(IComparable a, IComparable b){ return a.Equals(b); }
int number1 = 15, number2 = 9;
CompareTwoThings(number1, number2);    // This causes boxing and therefore generates garbage

- The workaround is to specify a value type constraint:

void CompareTwoThings<TComparable>(TComparable a, TComparable b) where  TComparable : stuct, IComparable{ return a.Equals(b); }


* Using ToString() on any object or value type


* Concatenating strings. Example:

string a = "hello", b = " sir.";
Print(a + b);


* Using the System.Convert class (In some cases, will elaborate later)


* Using the BitConverter class (Again, haven't checked all cases)


* Some collections that provides an IEnumerable<> that you can enumerate on using foreach().
Collections that are safe to itterate on include: Arrays, List


* Declaring an IEnumerable<> method that uses yield return(s)


* Passing and calling delegates to a method (this includes anonymous methods as well). Example:

    bool IsEven(int val) { return val % 2 == 0; }

    private delegate bool EvaluateValue(int val);

    void EvaluateSomething(EvaluateValue method, int loops)
    {
        for (int i = 0; i < loops; i++)
        {
            if (method(i))
            {
                //Debug.Log("Value " + i + " passed evaluation");
            }
        }
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            EvaluateSomething(IsEven, 32000);    // This call produced 52 bytes of garbage in my tests
        }
        if (Input.GetKeyDown(KeyCode.Period))
        {
            EvaluateSomething(delegate(int val) { return val % 2 == 0; }, 32000);    // Anonymous methods are the same generating 52 bytes of garbage
        }
    }



There's more stuff that I didn't have on top of my head, and not so much about unity yet; but I will fill in the missing things the next break I let my self have.
I will update this list as I stumble on little things that feeds the garbage collector and also correct myself for corner cases where I might have miss spoken.