We have four main types of things we'll be putting in the Stack and Heap as our code is executing: Value Types, Reference Types, Pointers, and Instructions.
Rules
A Reference Type always goes on the Heap.
Value Types and Pointers always go where they were declared. This is a little more complex and needs a bit more understanding of how the Stack works to figure out where "things" are declared.
The Stack, as we mentioned earlier, is responsible for keeping track of where each thread is during the execution of our code (or what's been called).
You can think of it as a thread "state" and each thread has its own stack. When our code makes a call to execute a method the thread starts executing the instructions that have been JIT compiled and and live on the method table, it also puts the method's parameters on the thread stack. Then, as we go through the code and run into variables within the method they are placed on top of the stack.
String.Empty and "" are almost the same, both refer to an existing string that has no content.
Said almost because, "" creates a temporary string in memory (to have something to compare against) while String.Empty is a language constant.
On the other hand, null means nothing, no object at all.
In more familiar terms, String.Empty is like having an empty drawer while null means no drawer at all!