There is no "subtraction", that would make calling a method entirely too expensive. A primary role here is played by the just-in-time compiler. It does not just translate MSIL to machine code, it also generates a table that describes how objects are used by the method. That table contains the addresses of the code locations where the object is used as well as where it is stored.
Note how the stack is an abstraction, the far more common place where an object reference is stored is in a processor register. The GC needs to know what register is used to properly track object usage. And the stack frame offset if it does get spilled to the stack.
When the garbage collector gets going then it walks the stack, traversing the stack frames of the active methods. And uses the table associated with each method to find the object references back. With the big advantage that nothing special needs to be done when a method completes, the stack frame simply is not there anymore. The table also makes garbage collection very efficient, an object can be collected even when the method has not yet finished executing. Matters a lot for, say, your Main() method, you would not want any object you use in that method to leak for the lifetime of the app. It makes the fixed
statement very cheap, the table simply has a bit that says that the object should not be moved.
The existence of that table is the distinction between managed and unmanaged code. More about that table in this answer.