We shall discuss the Stack memory in.NET in this article. The portion of memory in.NET that is assigned to a thread is called the Stack. To put it simply, each thread has a stack—that is, its own memory—that is the last in, first out. Note: Because there is a connection between Stack memory and Heap memory, some parts of the article are explained but do not include Heap memory.


Memory Stack
Computer memory architecture is where the idea of a memory stack first appeared. Its essence also affects the various programming languages. After all, in any computer system, a programming language is a form of abstraction or communication. Many programming languages are forced to adopt it as a result.
So, even the CPU has push-and-pop instructions for providing thread (stack) execution and other things. Well, without further confusion, let’s continue our look at .NET and their use of Stack memory.

Nearly all developers are aware that a data structure is referred to as a "stack." There are two general strategies for the stack data structure: FIFO and LIFO. We employ the LIFO (last in first out) method to operate our stack memory. Moreover, distinguish between stack memory and the stack data structure. Because stack memory makes use of the stack data structure technology, names are the same. There are two different kinds of memory in.NET; a heap and a stack, both of which are found in RAM. Method calls, return addresses, arguments/parameters, and local variables are all tracked by the stack. However, reference-type objects are stored in Heap (this is a topic for another article).

To put it simply, every thread has a memory location of its own. This is the stack, which represents each thread's local memory. Thus, during method execution, threads define a local variable, retrieve the result, and then call another method, and so on. It is these actions that require stack memory. Furthermore take note that just one thread of execution uses the stack memory. Conversely, a heap is shared memory used by multiple threads, the extent of which is specified by the operating system process.

Allocation of Stacks
Stack allocation, to put it simply, is a memory allocation technique that makes use of the stack, a section of memory. The most recently allocated memory is the first to be deallocated because, as I previously explained, the stack uses the LIFO (last in first out) method of operation. There are two types in.NET. A reference type is one, and a value type is the other. Thus, the value of the type is saved immediately when allocating a value type in a stack. However, when dealing with reference types that reside in heaps, it stores the heap's reference address, also known as the object's pointer. Generally speaking, it means that a stack only contains values (a reference address is a type of value, but we'll talk about that in another article).

The figure above illustrates how two declared variables are present in a separate stack frame within the thread stack: a flag (int32, value type) and a pInst, instance of the Person class. We won't talk about the heap, but the picture demonstrates that it has the flag's value of 1, as well as the reference address of pInst. The address for the stack that refers the heap object—an object with data and methods stored on the heap—is what is meant to be used in the reference type. In .NET, Stack memory has a fixed size which a 1-MB stack is allocated when a thread is created. This size is predefined once the program starts, providing a consistent amount of stack memory available for function calls and local variable storage. However, the fixed nature of the stack means that it can only hold a limited amount of data. In addition, the speed of stack memory operations is a key advantage based on the simplicity of the stack’s push and pop mechanisms.

So, almost everyone wonders why the Stack memory is fast.

  • Firstly, stack memory uses static memory allocation, meaning that memory is given and taken away in a predictable way. This removes the need for complicated memory management, making operations quick and efficient.
  • Secondly, as I said above the stack has a fixed size, which avoids the overhead of finding available memory space, unlike heap memory. The simple push-and-pop operations also make it faster. These operations just adjust the stack pointer, which is very fast compared to the more complex operations needed for heap memory.
  • Thirdly, modern CPUs have special instructions for stack operations, like pushing and popping data, which are made to be very fast. The CPU also uses a dedicated register, the stack pointer, to efficiently manage the stack’s top position. In addition, stack memory often gets loaded into the CPU cache. This happens because stack data is frequently accessed in a straight line, making it more friendly to the cache. In contrast, accessing heap memory is indirect and can involve more cache misses, making it slower. This direct hardware support and cache efficiency ensure that stack operations have very little overhead.

Stack Frame/Call Stack

A stack frame, also known as an activation record, is a section of the stack memory allocated for a single function/method call. When a function/method is called, a new stack frame is allocated, and when the function/method returns, its stack frame is deallocated.

Basically, a stack frame contains.

  • Function/Method Arguments: The values passed to the function/method.
  • Local Variables: Variables that are declared inside the function/method.
  • Return Address: The location in the code to return to after the function/method completes.


Let’s start the explanation with a code example.
public void Calculate()
{
    int a = 1;
    int b = 10;
    int c = a + b;

    Write(c);
    Console.WriteLine("Finished");
}

public void Write(int number)
{
    Console.WriteLine(number);
}


As you can see, there is a method named Calculate that declares 3 variables and is also called the Write method it.

Assume that, a thread calls the Calculate method, how would this be visualized?

As you can see from the image above, the first stack frame belongs to the Calculate method which stores 3 variables, one of which is the result of the sum of two variables. The second stack frame belongs to the Write method which stores the argument and return address (line 9 just for understanding, in fact, it contains different values).

So when the Write method completes the execution, the second stack frame is deallocated and the thread returns to the first stack frame that belongs to the Calculate method, through the return address it will execute from that address line and complete the execution of the Calculate method. In this way, the call stack loop that was called in this thread will continue to execute (in our case, execution is completed).

StackOverflowException

As I mentioned before, the stack size is limited, and for this reason, when any function/method is called in an unlimited and nested way, a “StackOverflowException” occurs.

Assume that we have some function/method that is using recursion but there is not any limitation. It means the method has some calculation in which there is no return state. So, in this case, we will get “StackOverFlowException”.

Let’s take a look at a code example.

public class Program
{
    public static void Main()
    {
        Calculate(2, 3);
    }

    public static void Calculate(int a, int b)
    {
        int c = a + b;
        Calculate(c, c * c);
    }
}


When you execute this code, you will get “StackOverFlowException” because recursion provides the call itself and there is no return state or limit in this function/method. With this technique, every call to the function/method creates new stack frames and when the stack limit is exceeded, we get a “StackOverFlowException”.

If you would like to learn more, stay tuned!