Mastering C# and Unity3D

Closures Without the GC

Closures allow you to save the local variables of a function and access them later in a callback. Think of how lambdas can access the local variables of the function they’re declared in, even though the lambda itself is another function. Unfortunately, creating a lambda like this creates garbage for the GC to collect and you have no control over that process. Today’s article introduces an alternative that allows you to take control over the GC and still use nice, type-safe closures. Read on to learn how!

First, we have to understand a little about how closures work in C#. Consider this function:

List.Find takes a delegate of type Predicate<T> which is defined as bool Predicate(T). We pass in a lambda i => i == val which satisfies that signature by taking a T/int and returning a bool. The magic is in the “closure”. Somehow the lambda function we’ve just written is able to access the val local variable of the enclosing function: Contains.

To make this possible, the C# compiler does a lot of work behind the scenes. What it actually does is create a new class with a field for each local variable the closure needs to access. It then puts the lambda function into that class as an instance function. If you use a decompiler like ILSpy on your Library/ScriptAssemblies/Assembly-CSharp.dll and look at the IL bytecode then you can see the generated class. Here’s what the class for Contains looks like (with annotations by me):

Using the lambda copies all the local variables the lambda uses to the class instance

Using the lambda creates a new delegate instance to wrap the lambda class’ instance function

Steps #2 and #4 create a total of 124 bytes of garbage because they’re instantiating new class objects. This is all code generated by the compiler, so we don’t really have any control over it. Which leads us to the point of today’s article: let’s get control.

So what if we were to manually create a lambda class? If we did that then we’d have control over when it gets instantiated and when it gets released to the GC. We could keep a single static instance of it around and reuse it or keep an object pool of them to have multiple at once. In either case we’d never release any references to them so there would never be any GC, unlike lambdas which are released automatically by compiler-generated code.

Since each lambda class needs to hold its own set of local variables, we can’t create a single class. Instead, we can create an interface similar to the delegate definition. Here’s the delegate again:

delegatebool Predicate<T>(T obj);

delegate bool Predicate<T>(T obj);

And here’s the interface version we’ll create:

interface IPredicate<T>{bool Invoke(T obj);}

interface IPredicate<T>
{
bool Invoke(T obj);
}

Obviously the List class doesn’t know about the interface we just created, so we’ll have to make our own version of Find in order to replicate the Contains function above. For your own code, you can just start with the interface version and skip the delegates. So here’s a version of Find that uses IPredicate:

Now that we have our own class, we have control over how it gets instantiated. There’s really no need to instantiate it every time Contains is called. For example, we can hold a single static reference to one:

Now there’s only one allocation for all calls to Contains. Since containsPredicate is readonly, it’s not even possible to release the reference to it. It will never be garbage-collected, which is great for avoiding frame spikes.

Alternatively, we could keep a pool of class instances. This is more appropriate for callback-type scenarios where you have an arbitrary number of them going at once. For simplicity, I’ll show a simple version of a pool using the same Contains function:

Notice that here too we never release any class instances. They always just go back into the pool, so none of them will ever be garbage-collected. Likewise, the pool itself is never released so there will be no GC for it, either.

It’s notable that we haven’t given up some key elements of delegates. We still have the type safety of generics. There’s no need to cast plain object variables to use this technique. If the predicate class is nested within another class, it can have access to its private fields. So there’s no need to make everything public to use this technique.

But there are definitely some differences between this technique with lambda closures, so let’s compare them. The major advantage of interfaces over lambdas is that you have total control over their lifecycle. You can easily make sure that they’re never garbage-collected and therefore avoid GC frame spikes. Second, calling an instance function is faster than calling a delegate, so there’s a minor speedup. Third, the code is now more straightforward because there’s nothing getting auto-generated by the compiler: we can see all of the code in the source files.

Of course the major downside is that we have to manually create our own predicate class. That means more boilerplate code to type out these classes and copy the local variables to their fields. We can forget to copy some local variables, so lambdas are less error-prone. Lambdas are also more compatible with the .NET and Unity APIs, so you won’t need to create wrappers to use them there.

As typical in programming, this technique offers a trade-off. Are you concerned about GC enough to make your own interfaces? Let me know in the comments if this is something you’d consider… or are already doing!