Starting with Unity version 2019.2.0f1, Unity stopped generating IL code capable of caching static delegates. Prior to that version, you could run the following code without generating any garbage:
{
public void Update()
{
this.DoCallback(StaticCallback);
}
private void DoCallback(Action action)
{
action();
}
private static void StaticCallback()
{
Debug.Log("Static callback");
}
}```
However, if you run the same code on any newer verison of Unity, the Update() method allocates a new delegate every call, creating unnecessary garbage.
Here is how Unity used to generate IL for the Update function before 2019.2:
```instance void Update () cil managed
{
.maxstack 8
// {
IL_0000: nop
// DoCallback(StaticCallback);
IL_0001: ldarg.0
IL_0002: ldsfld class [System.Core]System.Action GarbageTest::'<>f__mg$cache0'
IL_0007: brtrue.s IL_001a
IL_0009: ldnull
IL_000a: ldftn void GarbageTest::StaticCallback()
IL_0010: newobj instance void [System.Core]System.Action::.ctor(object, native int)
IL_0015: stsfld class [System.Core]System.Action GarbageTest::'<>f__mg$cache0'
IL_001a: ldsfld class [System.Core]System.Action GarbageTest::'<>f__mg$cache0'
// }
IL_001f: call instance void GarbageTest::smile:oCallback(class [System.Core]System.Action)
IL_0024: ret
}```
And here is how Unity now generates IL:
```instance void Update () cil managed
{
.maxstack 8
// {
IL_0000: nop
// DoCallback(StaticCallback);
IL_0001: ldarg.0
IL_0002: ldnull
IL_0003: ldftn void GarbageTest::StaticCallback()
IL_0009: newobj instance void [netstandard]System.Action::.ctor(object, native int)
IL_000e: call instance void GarbageTest::smile:oCallback(class [netstandard]System.Action)
// }
IL_0013: nop
IL_0014: ret
}```
As you can see, there is no longer any attempt to cache the static delegate, which means garbage is generated by every call to Update().
Why was delegate caching turned off in 2019.2? And can we get it turned back on?