As others have pointed out, the GC knows precisely which fields of every block on the stack and the heap are managed references, because the GC and the jitter know the type of everything.
However, your point is well-taken. Imagine an entirely hypothetical world in which there are two kinds of memory management going on in the same process. For example, suppose you have an entirely hypothetical program called "InterMothra Chro-Nagava-Sploranator" written in C++ that uses traditional COM-style reference-counted memory management where everything is just a pointer to process memory, and objects are released by invoking a Release method the correct number of times. Suppose Sploranator hypothetically has a scripting language, JabbaScript, that maintains a garbage-collected pool of objects.
Trouble arises when a JabbaScript object has a reference to a non-managed Sploranator object, and that same Sploranator object has a reference right back. That's a circular reference that cannot be broken by the JabbaScript garbage collector, because it doesn't know about the memory layout of the Sploranator object. So there is the potential here for memory leaks.
One way to solve this problem is to rewrite the Sploranator memory manager so that it allocates its objects out of the managed GC pool.
Another way is to use a heuristic; the GC can dedicate a thread of a processor to scan all of memory looking for integers that happen to be pointers to its objects. That sounds like a lot, but it can omit pages that are uncommitted, pages in its own managed heap, pages that are known to contain only code, and so on. The GC can make a guess that if it thinks an object might be dead, and it cannot find any pointer to that object in any memory outside of its control, then the object is almost certainly dead.
The down side of this heuristic is of course that it can be wrong. You might have an integer that accidentally matches a pointer (though that is less likely in 64 bit land). That would extend the lifetime of the object. But who cares? We are already in the situation where circular references can extend the lifetimes of objects. We're trying to make that situation better, and this heuristic does so. That it is not perfect is irrelevant; it's better than nothing.
The other way it can be wrong is that Sploranator could have encoded the pointer, by, say, flipping all of its bits when storing the value and only flipping it back right before the call. If Sploranator is actively hostile to this GC heuristic strategy then it doesn't work.
Resemblance between the garbage collection strategy outlined here and the actual GC strategy of any product is almost entirely coincidental. Eric's musings about implementation details of garbage collectors of hypothetical non-existing products are for entertainment purposes only.