Search code examples
c#.netgarbage-collectionudplow-latency

How long can I typically keep GC mode set to SustainedLowLatency


I have a server app which receives lots of UDP packets from several sensors 50 times per second (new packet every 20ms), does some analysis, stores them, does some debug logging and other "server stuff".

The problem is that the GC performs a full blocking collection every once in a while, suspending all threads for up to 200ms (perhaps even more in some rare percentiles). I don't have a problem with lagging behind each packet for couple of milliseconds (even sustained latency of say 10ms for each packet wouldn't be an issue), but long suspends are really annoying.

According to MSDN, there is the SustainedLowLatency mode for the GC, which, according to MSDN:

Enables garbage collection that tries to minimize latency over an extended period. The collector tries to perform only generation 0, generation 1, and concurrent generation 2 collections.

Full blocking collections may still occur if the system is under memory pressure.

  1. Does "extended period" still mean I cannot simply set the mode to SustainedLowLatency and forget it?

  2. Is there any way to prevent full blocking collections, at least for a single core?


Solution

  • Basically, this mode will try to use only background gen 2 collections, and do a full GC only when the system starts lacking memory. The delay between blocking collections depends entirely on your code: the less you use the gen 2 the less the memory will be fragmented, and the longer you'll last. Unfortunately, sockets use pinned buffers, which are a typical cause of memory fragmentation. You can try to reuse your buffers, but it involves writing tricky low-level code with the socket API.

    The only way to prevent full blocking collections is to use TryStartNoGCRegion. There again, how much you can last depends on how much memory you allocate. Use small objects. Use structs instead of classes whenever possible. Use pooling whenever you need large arrays.

    Profiling (for instance, with Jetbrains dotTrace) will help you spot and optimize codepaths that allocate a lot of memory.