I have found some great articles (Maoni, Richter #1, Richter #2) giving many details as to the theory and practice of the GC, yet I cannot find anything that states how the
the garbage collector runs at lower priority than critical threads
The thread that the GC runs on depends on which version of the GC your app is using.
The answer to this question is contained in a blog post I've written: How to determine which garbage collector is running
In CLR via C#, Richter explains that:
A special high-priority CLR thread is dedicated to calling
Finalize
methods
(see the "Finalization Internals" heading of chapter 20)
This is the only context in which he talks about a garbage collector thread. A little earlier in the chapter, he explains that garbage collection is started in response to one of the following events:
GC.Collect
...which suggests that the only thread created by the garbage collector is this single, "high-priority" finalizer thread.
Edit: He then goes on, in "Concurrent Collection", to explain that:
On a multiprocessor system running the workstation version of the execution engine, the garbage collector has an additional background thread to collect objects concurrently while the application runs. [...] The garbage collector has a normal priority background thread that marks unreachable objects.
The GC thread priority is an implementation detail, right now they are running in normal priority, however the finalizer thread is running as a high priority and has a timeout as well.
The GC thread runs at a normal priority. The finalizer thread runs at "Highest" priority.
You can see this by turning on the Debug "Thread" window, and breaking anywhere in a managed application. The threads are all listed (although they're not named), with their priorities. It takes a bit to decipher which is which, but there will be an extra "Normal" and "Highest" priority thread, which correspond to the GC and the Finalizer thread.