A root is a storage location, such as local variable, that could contain a reference and is known to have been initialized, and that your program use at some point in the future without needing to go via some other object reference.
In Programming C# 10.0 by Ian Griffiths in the seventh chapter it is stated that:
A root is a storage location, such as a local
variable, that could contain a reference and is known to have been
initialized, and that your program could use at some point in the future
without needing to go via some other object reference. Not all storage
locations are considered to be roots. If an object contains an instance field
of some reference type, that field is not a root, because before you can use
it, you’d need to get hold of a reference to the containing object, and it’s
possible that the object itself is not reachable. However, a reference type
static field is a root reference, because the program can read the value in
that field at any time.
additionally in this document by Microsoft it is said that:
An application's roots include static fields, local variables on a thread's stack, CPU registers, GC handles, and the finalize queue. Each root either refers to an object on the managed heap or is set to null. The garbage collector can ask the rest of the runtime for these roots. The garbage collector uses this list to create a graph that contains all the objects that are reachable from the roots.
According to C# 12 in a Nutshell by Joseph Albahari, Root A root is something that keeps an object alive. If an object is not directly or indirectly referenced by a root, it will be eligible for garbage collection.
The figure below from the same book can help in understanding: