Photo generated by Gemini

So you want your C# code to run fast, like, really fast, for a brief moment? Maybe you're processing real-time audio, handling network packets, or doing some time-sensitive game logic where even a tiny pause is unacceptable.
This is where GC.TryStartNoGCRegion() could come in handy for you :D
What Are We Trying To Solve?
Normally, .NET's GC runs whenever it feels like cleaning up unused memory. Most of the time, this is awesome right, you don't have to manually manage memory and everything just works. Not quite though. Sometimes, GC picks the worst possible moment to pause your program and do its cleanup work
Imagine you're in the middle of processing a chunk of audio data that needs to go out to the speakers right now, and suddenly the GC decides to stop everything for 5-10 ms to collect garbage. Your audio stutters and your users are sad...
How Can You Achieve That?
Setting up your budget
First of all, you want to set up your memory budget:
const long bBytes = 2 * 1024 * 1024 // 2MB in this case
You're telling the runtime: "Hey, I'm about to do some work, and I promise I won't allocate more than 2MB of new memory" This is your budget. Stay under it, and the GC won't interrupt you
Entering the No-GC Zone
if (!GC.TryStartNoGCRegion(bBytes))
{
Console.WriteLine("Could not start...");
return;
}
This is where you actually ask the runtime to disable garbage collection. The Try part is important because this can fail
if there's not enough free memory available, or a GC is already running, or generally speaking the runtime can't just guarantee it.
Just remember that if it returns false, bail out gracefully. Don't force it
Do Your Important Work
byte[] data = new byte[1024];
Span<byte> buffer = data;
try
{
for (int i = 0; i < 100_000; i++)
{
DoWork(ref buffer);
}
}
Now everything's running while GC is taking a break :D
Very important note: We're using a Span<byte> here. This is super important because Span is a stack-allocated type
that doesn't create heap allocations. If you were creating new objects, strings, or arrays, in this loop, you'd blow your
budget real quick and the GC would wake up anyway
Always Clean Up
You definitely need a finally block to make sure that no matter what happens, you tell the GC it can wake up and resume normal operations
If for any case you exceeded your budget during the no-GC region, the runtime might have already been forced to trigger
a collection. In that case, EndNoGCRegion() will throw an InvalidOperationException to let you know you broke your promise
finally
{
try
{
GC.EndNoGCRegion();
}
catch (InvalidOperationException ex)
{
Console.WriteLine("Budget exceeded: " + ex.Message);
}
}
What's Happening Under the Hood?
There are certain things that happen under the hood for everything to work out as expected. Here's the gist:
- ◆ The runtime examines the current heap state and verifies there's enough free memory to satisfy your budget without needing a collection
- ◆ It essentially locks a chunk of memory for your use, to make sure that even if you allocate up to your budget, there won't be a need to collect garbage
- ◆ The normal behaviors that trigger GC are obviously disabled, such as "we've allocated X bytes since last GC" or "we're running low on memory"
- ◆ You do your thing and the GC won't interrupt you, but you must stay within your budget
- ◆ Don't forget to re-enable GC
When Should You Use It?
Finally, this will help you understand when could you even use this, perhaps, new knowledge you've just gained.
Some good cases would be real-time audio/video processing, trading, game engine critical paths, network processing, parsing etc
Some bad cases would be long-running applications, when you're allocating a lot of objects, normal application code(premature optimization is the root of all evil), I/O bound operations because obviously GC pauses aren't your bottleneck