OK, I realize that question might seem weird, but I just noticed something that really puzzled me... Have a look at this code :
static void TestGC()
{
Could someone shed some light on what's going on?
Your mental model of how the virtual machine garbage collects resources is unrealistically simple. In particular, your assumption that variables and scope are somehow relevant to garbage collection is wrong.
Is this related to JIT optimizations?
Yes.
What's happening exactly?
The JIT didn't bother keeping references around unnecessarily so the tracing collector didn't find references when it kicked it so it collected the objects.
Note that other answers have stated that the JIT conveyed this information to the GC when the truth is actually that the JIT did not convey those references to the GC because there would be no point in doing so.